ADL找不到模板化的自由函数
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
内部,如果存在std::
自由函数,using std::to_string
将使用它,否则将使用My::
。然后,在名称空间My
之外,执行using My::to_string
就足以覆盖这两种情况。到目前为止还好。
然后,我在成员函数A::to_string
中使用了相同的技术,以避免更喜欢函数本身而不是自由函数(这同样适用于任何其他成员函数)。
最后,我对to_string(vector<A>)
的编译感到有点惊讶,尽管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
s和pair
s替换为Vector
s和Pair
s。(不再需要自由函数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>)
。
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<...>)
- 将自由函数绑定为类成员函数
- 如何将成员函数作为回调参数传递给需要"typedef-ed"自由函数指针的函数?
- 为什么成员函数地址离自由函数这么远?
- 如何使用模板元编程在自由函数C++链接两个不相关的类
- C++ 20 中的运算符 == 和 <=> 应该作为成员还是自由函数实现?
- 如何在共享库中保留静态库中的自由函数
- 在C++项目中管理自由函数有哪些好的做法?
- 使用自由函数作为LPOVELAPPED_COMPLETION_ROUTINE
- 统一检查成员函数、自由函数和算子是否存在的方式
- 使自由函数的行为类似于成员函数 (C++)
- C++自由函数可以别名吗?
- 调用成员函数(如果存在),回退到自由函数,反之亦然
- ADL找不到模板化的自由函数
- 基于方法或自由函数存在的模板构造函数解析
- G 未定义的引用自由函数
- 为什么未调用自由函数作为构造函数中的参数传递
- 自由函数和继承
- 调用自由函数而不是方法(如果不存在)
- 如何将自由函数与对象一起使用
- 如何使用begin()自由函数