[英]ADL does not find templated free function
我試圖了解ADL的工作原理,至少是基礎知識,並創建了以下代碼:
#include <iostream>
#include <string>
#include <vector>
using std::pair;
using std::string;
using std::vector;
namespace My {
using std::to_string;
/** With these forward declarations it works ...
string to_string(char arg);
const string& to_string(const string& str);
template <typename T>
string to_string(const vector<T>& rhs);
template <typename T1, typename T2>
string to_string(const pair<T1, T2>& rhs);
struct A;
string to_string(const A& rhs);
*/
string to_string(char arg)
{
return string(1, arg);
}
const string& to_string(const string& str)
{
return str;
}
template <typename T>
string to_string(const vector<T>& rhs)
{
string str("");
for (const auto& e : rhs) {
str += to_string(e) + " "; //< this fails with `pair<..>`
/// `(to_string)(e)` would fail even with `A`
}
return str;
}
template <typename T1, typename T2>
string to_string(const pair<T1, T2>& rhs)
{
return to_string(rhs.first) + " " + to_string(rhs.second);
}
struct A {
static int counter;
string to_string() const
{
using My::to_string; //< avoid using `A::to_string`
return to_string("<A>[") + to_string(counter++) + to_string("]");
}
};
int A::counter = 0;
string to_string(const A& rhs)
{
return rhs.to_string();
}
}
int main(int /*argc*/, const char* /*argv*/[])
{
using My::to_string;
using My::A;
using std::cout;
using std::endl;
cout << to_string(3.1415) << endl;
cout << to_string(pair<int, char>{5, 'a'}) << endl;
cout << to_string(pair<double, pair<int, int>>{3.14, {1, 2}}) << endl;
cout << to_string(vector<int>{1, 2, 3}) << endl;
cout << to_string(pair<string, vector<int>>{"key", {1, 2, 3}}) << endl;
cout << to_string(pair<string, A>{"key", {}}) << endl;
cout << to_string(vector<A>{{}, {}, {}}) << endl;
/// this will fail to compile
cout << to_string(vector<pair<string, int>>{{"a", 1}, {"b", 2}}) << endl;
return 0;
}
我發現在My
里面, using std::to_string
將使用std::
free函數(如果存在),否則將使用My::
。 然后,在My
命名空間之外, using My::to_string
覆蓋這兩種情況就足夠了。 到目前為止很好。
然后,我在成員函數A::to_string
使用了相同的技術,以避免偏向於函數本身而不是自由函數(其他成員函數也是如此)。
最后,盡管沒有預先聲明A
, to_string(vector<A>)
使我有些驚訝。 據我所知,這就是ADL發揮作用的地方。 禁用它(將to_string
括在方括號中)將導致編譯失敗。
現在,經過漫長的故事之后,我想到了一個問題:為什么在這種情況下ADL對於模板化函數(即to_string(pair<T1, T2>)
不起作用? 更重要的是,如何解決? 如果不需要執行前向聲明,我會很高興,因為在我的用例中, to_string(vector<T>)
的定義位於某些基本頭文件中,而后者的定義在不同的頭中,因此不應目前已知。
編輯 :
我試圖通過某些模板甚至使用某些SFINAE,以某種方式“偽造”所需的前向聲明,但是這導致了歧義或相同的結果。 最后,我想出了使用所需類的成員函數to_string
(但可以是任何其他名稱)的解決方案。 如果需要與to_string
兼容,則這需要始終實現此功能,如果是STL容器,則需要繼承它們並添加成員函數。 但是我相信,在這種情況下,ADL將永遠不會失敗。
這是代碼的修改部分:
template <typename T,
typename Fun = decltype(&T::to_string),
typename = std::enable_if_t<
std::is_member_function_pointer<Fun>::value>>
string to_string(const T& rhs)
{
return rhs.to_string();
}
template <typename T>
struct Vector : public vector<T> {
using vector<T>::vector;
string to_string() const
{
using My::to_string; //< avoid using `Vector::to_string`
string str("");
for (const auto& e : *this) {
str += to_string(e) + " ";
}
return str;
}
};
template <typename T1, typename T2>
struct Pair : public pair<T1, T2> {
using pair<T1, T2>::pair;
string to_string() const
{
using My::to_string; //< avoid using `Pair::to_string`
return to_string(this->first) + " " + to_string(this->second);
}
};
但是,必須用Vector
和Pair
替換vector
和pair
。 (不再需要自由函數to_string(A)
)。
其他解決方案,有何評論?
為什么ADL在這種情況下不適用於模板函數,即
to_string(pair<T1, T2>)
?
ADL通過檢查與給定調用中涉及的類型關聯的名稱空間來工作。 然后,它將考慮在這些命名空間中找到的適當重載,並選擇最佳的重載。
to_string(vector<pair<string, int>>{{"a", 1}, {"b", 2}})
該調用將選擇重載My::to_string(const vector<T>&)
然后to_string(std::pair<std::string, int>)
調用to_string(std::pair<std::string, int>)
。
然后,ADL檢查與std::pair
, std::string
和int
相關聯的名稱空間,以查找重載to_string(std::pair<...>)
。 由於在命名空間std
沒有定義這樣的重載,因此需要在調用之前找到定義,但是重載My::to_string(const pair<T1, T2>&)
是在之后定義的。 這就是為什么您需要轉發聲明它的原因。
注意,您需要轉發聲明它,因為您還具有以下內容:
to_string(pair<string, vector<int>>)
另一方面,如果您有類似以下內容:
to_string(vector<pair<string, My::A>>{{"a", {}}, {"b", {}}})
然后,該調用的關聯名稱空間之一將是My
本身,並且無需轉發聲明即可找到重載to_string(std::pair<...>)
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.