发生这种奇怪的行为时,我正在弄乱一些代码:
在第一个测试中,基本模板化函数,用户和模板化专业化位于同一名称空间内,并且其行为符合我的预期:
namespace Test1
{
template <typename V, typename T>
int doFoo(V& a_visitor, T& a_value)
{
return 0;
}
struct Foo
{
template <typename T>
int process(T const& a_value)
{
return doFoo(*this, a_value);
}
};
template <typename T>
int doFoo(Foo& a_vis, T const& a_ptr)
{
return 1;
}
}
int main()
{
int const k{ 42 };
return Test1::Foo{}.process(k); // returns 1
}
但是当我将基本模板化的函数及其特化移到另一个名称空间时,就选择了基本的模板函数:
namespace Test2
{
namespace b
{
template <typename V, typename T>
int doBar(V& a_visitor, T& a_value)
{
return 0;
}
}
struct Bar
{
template <typename T>
int process(T const& a_value)
{
return b::doBar(*this, a_value);
}
};
namespace b
{
template <typename T>
int doBar(Bar& a_vis, T const& a_ptr)
{
return 1;
}
}
}
int main()
{
int const k{ 17 };
return Test2::Bar{}.process(k); // returns 0
}
编辑,我什至可以做得更奇怪:在示例1中,如果将对doFoo
的调用替换为Test1::doFoo
,我又会得到错误的提示!
有人可以解释一下这是怎么回事吗?如果我真的需要struct Bar不在名称空间b中怎么办?
答案 0 :(得分:2)
首先,这些不是专长,而是重载。互不相关的完全不同的功能模板。
您看到的行为与argument-dependent lookup一致。当遇到不合格函数调用时,编译器将通过检查与函数调用的每个参数关联的命名空间来构建重载集。
通常这不会在“之后”找到声明,但是模板是特殊的。在模板中查找依赖名称,例如依赖于模板参数(a_value
类型的函数调用),是在实例化模板之后而不是在定义时执行的。在命名空间完成并且所有重载都可用之后,main
中就会发生这种情况,因此ADL会找到第二个重载。
这也是为什么当您通过Test1
进行呼叫限定时,您不再找到第二个过载的原因。否定ADL,仅允许出现在调用点之前的重载。解决该问题的最简单方法可能是延迟process
的定义,直到所有重载都可用为止,如其他答案所示。