知识回顾:
Argument Dependent Lookup(Koenig Lookup)
当函数调用时,根据参数类型定义所在的命名空间或类域中查找函数名或运算符。如下所示。
namespace NS
{
struct A{};
void f(A);
}
int main()
{
NS::A a;
f(a);
}
在main中,并未在main所在的全局域中给出函数f的定义和声明,但是这段代码仍然可以通过编译,这正是Argument Dependent Lookup规则在起作用,在确认f(a)为函数调用时,编译器便开始实施Argument Dependent Lookup规则。
对于模板函数,这种查找规则仍然有效。例如
namespace NS
{
struct A{};
template<typename T> void f(T);
}
int main()
{
NS::A a;
f(a);
}
在上面的代码中,如果显示指定NS::f的模板参数会发生什么情况呢?
f<NS::A>(a);
程序无法编译通过!貌似Argument Dependent Lookup不起作用了?确切地说现在编译器并没有实施这种查找规则。在编译器的眼中,这行语句并不是函数调用,而是被看作了
(f < NS::A) > (a);
用小于比较f与NS::A,然后其结果再与a进行大于比较。导致这种情况是因为<NS::A>的部分没有被看作是显示指定的模板参数,因为在main所在的全局域中,并没有函数模板f的声明,也没有任何信息说明f是函数模板。解决的办法就是
NS::f<NS::A>(a);
现在编译器从f的声明得知f是函数模板,那么<NS::A>也理所当然的被看作是显示指定模板参数的列表。
问题:
代码如下。
namespace NS1
{
struct A{};
template<typename T> void f(A){}
}
namespace NS2
{
template<int I> void f(NS1::A){}
}
int main()
{
NS1::A a;
using NS2::f;
f<NS1::A>(a);
}
思考这段代码,编译器应该如何处理f<NS1::A>。
1. 编译失败,f无法和NS1::A进行比较。
2. 编译失败,无法匹配NS2::f的参数,需要一个非类型的模板参数。
3. 编译失败,f未定义。
4. 编译成功,调用NS1::f。
5. 编译成功,调用NS2::f。
6. 编译失败,二义性,有NS1::f和NS2::f的候选匹配。
答案:
4. 编译成功,调用NS1::f。
解析过程:
首先 using-declaration将NS2:f 引入到main函数域中,然后对于f<NS1::A>(a)这一句,此刻编译器就能识别到f是一个函数模板,确认<NS1::A>为显示指定模板参数,接下来就能确认f<NS1::A>(a)是函数调用。在确认是函数调用之后,开始命名查找,首先查找到NS2::f,这是由using-declaration引入到main函数域中的。还没完,接下来开始Argument Dependent Lookup,a的类型是定义在NS1中的,还需要在NS1中继续查找f,最后找到了NS1::f和NS2::f,而NS2::f的模板参数为非类型模板参数。最符合匹配的就只有NS1::f。