【发布时间】:2018-07-18 22:41:11
【问题描述】:
出于说明目的,假设我有这个人为的容器:
template <typename T>
struct Container {
T v;
};
而这个人为的“价值:”
struct Value {};
我有一个递归模板函数,声明完全一样:
template <typename T>
void visit(const Container<T> &c) {
visit(c.v);
}
template <typename T>
void visit(const T &v) {}
根据上述情况,调用visit(Value{}) 是完全有效的。我也可以调用:
void this_works_too(Container<Value> c) {
visit(c);
}
这有点令人费解,因为递归调用visit(c.v)发生在它的定义之前。
但是,当我们尝试以下操作时,我们得到一个错误(来自 clang 6 和 gcc 8):
void this_blows_up(Container<int> c) {
visit(c);
}
它现在抱怨递归调用visit(c.v):
error: call to function 'visit' that is neither visible in the
template definition nor found by argument dependent lookup
但是,如果我们重新排序 visit 的声明:
template <typename T>
void visit(const T &c) {}
template <typename T>
void visit(const Container<T> &c) {
visit(c.v);
}
this_works_too 和 this_blows_up 都编译成功。
(此行为也发生在 STL 容器中,与 const 和引用限定符无关)
为什么对于visit(Container<int>) 的专业化来说,订单对visit(Container<Value>) 不重要?
在我的研究中,我怀疑这必须处理ADL(我承认我不完全理解)。但我对此的最佳解释是,在搜索结束时Container<int> 的情况下,由于普通的unqualified lookup,该集合应该找到int。所以,Container<int> 和 Container<Value> 都应该可以工作。
我已经确认,尽管重新排序,但调用了正确的重载(即,当 visit(T) 是第一个时,visit(Container<T>) 实际上是为 visit(Container<int>) 调用的,而不是仅仅选择更通用的 visit(T))。我相信这是因为选择了最具体的专业。
【问题讨论】:
标签: c++ templates c++17 overloading