我試圖以一種復雜的方式實體化一個多載的模板函式,我發現它會被實體化,除非它在命名空間中。無論哪種方式,編譯都會成功,但如果函式未實體化,則無法鏈接。
我可以通過重新排序程式來解決這個問題,以便在呼叫之前定義多載,但這實際上是我想要避免的。
我猜這與ADL有關。
我試圖創建一個最小的可重現示例——如果我進一步簡化任何事情(例如用本機型別替換 SFyp),命名空間版本和非命名空間版本之間將不再有區別。
SZug<T, A>.zug()forwards to baz<T, A>()which forwards to foo<T, A>()(foo稍后多載)。
SFyp只是一個用于T(本機型別不會產生相同行為)的結構。
main()來電SZug<T, A>.zug()
如果foo(及其多載)在命名空間中定義,foo則不會實體化。否則,它會。
ns_used.cpp
// Forwarding chain: SZug.zug() -> baz() -> foo().
namespace ns
{
template <typename T1, typename T2>
void foo(T1 v1, T2 v2); // This is overloaded by SFyp
}
template <typename T1, typename T2>
void baz(T1 v1, T2 v2)
{ ns::foo(v1, v2); }
template <typename T1>
struct SZug
{
template <typename T2>
void zug(T2 v2)
{
T1 v1;
baz(v1, v2);
}
};
// SFyp overload of foo().
struct SFyp{};
namespace ns
{
template <typename T2>
void foo(SFyp fyp, T2 v2)
{}
}
// Test.
int main()
{
SZug<SFyp> z;
z.zug(1);
}
g -o ns_used.run ns_used.cpp鏈接失敗:
clang -o ns_used.run ns_used.cpp鏈接失敗:
in function `void baz<SFyp, int>(SFyp, int)':
ns_used.cpp:(.text._Z3bazI4SFypiEvT_T0_[_Z3bazI4SFypiEvT_T0_] 0xf): undefined reference to `void ns::foo<SFyp, int>(SFyp, int)'
ns_unused.cpp
// Forwarding chain: SZug.zug() -> baz() -> foo().
//namespace ns
// {
template <typename T1, typename T2>
void foo(T1 v1, T2 v2); // This is overloaded by SFyp
// }
template <typename T1, typename T2>
void baz(T1 v1, T2 v2)
{ /*ns::*/foo(v1, v2); }
template <typename T1>
struct SZug
{
template <typename T2>
void zug(T2 v2)
{
T1 v1;
baz(v1, v2);
}
};
// SFyp overload of foo().
struct SFyp{};
//namespace ns
// {
template <typename T2>
void foo(SFyp fyp, T2 v2)
{}
// }
// Test.
int main()
{
SZug<SFyp> z;
z.zug(1);
}
g -o ns_unused.run ns_unused.cpp成功。
clang -o ns_unused.run ns_unused.cpp成功。
注意:除了注釋掉的命名空間之外,它們是相同的。
而且,如上所述,如果我將SFyp其foo多載放在 之前baz,無論有沒有命名空間,它都會成功。
這種奇怪行為的原因是什么?是ADL嗎?
有沒有辦法解決這個問題,以便在baz尋找 a時考慮我的多載foo,同時仍然使用命名空間?
uj5u.com熱心網友回復:
TLDR
ADL 用于查找不合格的函式名,例如or foo,而不是合格的函式名。ns::foo::foo
這種奇怪行為的原因是什么?
問題是 ADL 不能用于限定名稱,如ns::fooor ::foo。這就是如果您在第二個示例中替換為的原因foo,::foo那么您將得到完全相同的錯誤。演示。而且您只提供了主函式模板的宣告而不是定義,而且您在定義之后提供了多載,因此合格的查找無法找到該多載的定義。而且我們現在知道 ADL 不會發生,本質上我們在最后得到了缺少定義的聯結器錯誤fooSFypbazfoo由于限定查找只找到了一個宣告,因此無法從該宣告實體化定義。這意味著只有宣告template<> void ns::foo<SFyp, int>(SFyp, int)被實體化。這正是聯結器錯誤所說的。
另一方面,對于像fooADL 這樣的非限定名稱也可以使用。這就是您的第二個示例有效的原因。基本上,在您的第二個示例中,將發生 ADL,以便聯結器具有可用的定義,因此在您的第二個示例中不會出現錯誤。
有3種方法可以解決這個問題。解決此問題的第三種方法是簡單地為主要功能模板提供定義。演示。其他兩個在答案末尾給出。
這可以從ADL 檔案中看出:
依賴于引數的查找,也稱為 ADL 或 Koenig 查找 [1],是用于在函式呼叫運算式中查找非限定函式名稱的一組規則,包括對多載運算子的隱式函式呼叫。
(強調我的)
這意味著由于ns::foo和::foo是名為 的限定函式,因此此處不會發生 ADL。但是,如果您只使用不合格的函式名 foo,那么 ADL 可以作業(正如您在第二個示例中所注意到的)。
從basic.lookup.argdep中也可以看到相同的內容:
當函式呼叫中的后綴運算式是 unqualified-id時,可能會搜索在通常的非限定查找期間未考慮的其他名稱空間,并且在這些名稱空間中,名稱空間范圍的友元函式或函式模板宣告 ([class.friend]) 不會否則可能會發現。
(強調我的)
這意味著,由于在您的第一個示例中,后綴運算式ns::foo是一個限定 ID,因此 ADL 不能為此作業,并且您會收到上述錯誤。但是在您的第二個示例foo中是一個不合格的 ID,因此 ADL 可以在這里作業,因此您的第二個示例可以作業。
解決方案
有兩種方法可以解決這個問題,如下所示:
方法一
下面是我SFyp在全域命名空間中前向宣告并在命名空間中宣告函式模板void foo(SFyp fyp, T2 v2)的作業解決方案,ns以便呼叫所需的多載。
#include <iostream>
#include <type_traits>
#include <vector>
struct SFyp; //I ADDED THIS
namespace ns
{
template <typename T1, typename T2>
void foo(T1 v1, T2 v2);
template <typename T2> //I ADDED THIS
void foo(SFyp fyp, T2 v2);
}
template <typename T1, typename T2>
void baz(T1 v1, T2 v2)
{ ns::foo(v1, v2); }
template <typename T1>
struct SZug
{
template <typename T2>
void zug(T2 v2)
{
T1 v1;
baz(v1, v2);
}
};
// SFyp overload of foo().
struct SFyp{};
namespace ns
{
template <typename T2>
void foo(SFyp fyp, T2 v2)
{std::cout <<"desired overload called";} //I ADDED THIS
}
// Test.
int main()
{
SZug<SFyp> z;
z.zug(1);
}
演示
修改后的程式的輸出是:
desired overload called
方法二
第二種方法是將foo多載移到baz定義之前,如下所示:
namespace ns
{
template <typename T1, typename T2>
void foo(T1 v1, T2 v2);
}
//-------------------I MOVED THIS HERE-----------------------------------//
struct SFyp{};
namespace ns
{
template <typename T2>
void foo(SFyp fyp, T2 v2)
{std::cout << "desired overload";}
}
//------------------------------------------------------//
template <typename T1, typename T2>
void baz(T1 v1, T2 v2)
{ ns::foo(v1, v2); }
template <typename T1>
struct SZug
{
template <typename T2>
void zug(T2 v2)
{
T1 v1;
baz(v1, v2);
}
};
int main()
{
SZug<SFyp> z;
z.zug(1);
}
作業演示
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/515127.html
上一篇:模板類c 中的靜態變數
