Bai*_*ker 1 c++ templates overloading c++17
为了便于说明,请说我有这个有人工作的容器:
template <typename T>
struct Container {
T v;
};
Run Code Online (Sandbox Code Playgroud)
这个人为的"价值:"
struct Value {};
Run Code Online (Sandbox Code Playgroud)
我有一个递归模板函数声明完全如下:
template <typename T>
void visit(const Container<T> &c) {
visit(c.v);
}
template <typename T>
void visit(const T &v) {}
Run Code Online (Sandbox Code Playgroud)
有了上述内容,调用它是完全有效的visit(Value{}).我也可以调用:
void this_works_too(Container<Value> c) {
visit(c);
}
Run Code Online (Sandbox Code Playgroud)
这有点令人费解,因为递归调用visit(c.v)在其定义之前发生.
但是,当我们尝试以下操作时,我们会收到错误(来自clang 6和gcc 8):
void this_blows_up(Container<int> c) {
visit(c);
}
Run Code Online (Sandbox Code Playgroud)
它现在抱怨递归调用visit(c.v):
error: call to function 'visit' that is neither visible in the
template definition nor found by argument dependent lookup
Run Code Online (Sandbox Code Playgroud)
但是,如果我们重新排序声明visit:
template <typename T>
void visit(const T &c) {}
template <typename T>
void visit(const Container<T> &c) {
visit(c.v);
}
Run Code Online (Sandbox Code Playgroud)
这两个,this_works_too和this_blows_up编译,成功.
(对于STL容器也会发生此行为,并且const与参数限定符无关)
为什么订单在专业化visit(Container<int>)时变得非常重要visit(Container<Value>)?
在我的研究中,我怀疑这必须处理ADL(我承认我并不完全理解).但我对此的最佳解释是,在搜索结束时Container<int>,该集应该int由于普通的不合格查找而找到.所以,无论是Container<int>和Container<Value>应该工作.
我已经确认,尽管重新排序,但是调用了正确的重载(即,当visit(T)第一个visit(Container<T>)被调用时,实际上是调用visit(Container<int>)而不仅仅是选择更通用的visit(T)).我相信这是因为选择了最具体的专业化.
小智 5
在我的研究中,我怀疑这必须处理ADL(我承认我并不完全理解).
你是对的,这就是问题所在.
但我对此的最佳解释是,在搜索结束时
Container<int>,该集应该int由于普通的不合格查找而找到.
普通的非限定查找无法看到template <typename T> void visit(const T &v)重载,因为尚未声明它.依赖于参数的查找是一个例外; 它可以在实例化时看到所有声明.
该类型int是与任何特定命名空间无关的内置类型.因为int没有任何关联的命名空间,所以没有ADL搜索声明的命名空间,因此无法找到第二个声明.
该类型Value是全局命名空间中的用户定义类型.因此,ADL在全局命名空间中搜索声明,在这种情况下,它确实找到第二个声明.