Keonig Lookup 何时导致 ambiguous? 何时不会导致该问题?

 

#include "stdafx.h"
#include <iostream>
using namespace std;

namespace NS {
class T { };
void f(T)
{
cout <<"NS::f()" <<endl;
}
}

void f(NS::T)
{
cout <<"Global::f()" <<endl;
}

NS::T parm;

int main()
{
f(parm); // 这里会引起 ambiguous 问题, 这是因为
// f 的参数 'param' 是一个类, 所以通过
// ADL(argument-dependent lookup) 找到了
// NS 命名空间中的 NS::f. 两个函数都可以
// 匹配当前的语句, 因此存在二义性.
}


那么我们有如下修改:

#include "stdafx.h"
#include <iostream>
using namespace std;

namespace NS {
class T { };
void f(T)
{
cout <<"NS::f()" <<endl;
}
}

void f(NS::T)
{
cout <<"Global::f()" <<endl;
}

NS::T parm;

int main()
{
void f(NS::T);
f(parm); // 调用了哪个 f()? 为何调用了 Globle::f()?
}


解释: 

    主楼第一个例子中的二义性源于函数重载存在二义性。具体过程是,当编译器解析到 f(param) 这句时,编译器能够确定 f 是一个函数名字,因此编译器需要为 f 生成重载函数集合。因为 f 的具体用法为一个 unqualified name,所以首先执行 unqualified name lookup,此过程将 ::f 加入重载函数集合。然后编译器意识到 f(param) 的调用中 param 为一个类类型变量,根据现有条件,需要执行 argument-dependent lookup,后者将 NS::f 加入重载函数集合。因此最终的重载函数集合是 {::f,NS::f}。重载函数集合生成后,函数重载解析发生,因为 ::f 和 NS::f 的形参列表相同,且都能够匹配当前调用,因此重载解析无法决议(ambiguous),调用失败。

    主楼第二个例子与第一个例子的区别在于,unqualified name lookup 后的现有条件(上段红色文字)两者不同。此时重载函数集合中包含的 f 是声明于 main 函数定义作用域中的函数。根据 C++11 标准,在这种情况下,argument-dependent lookup 不会向已有重载函数集合中加入任何东西。因此最终的重载函数集合为 {当前作用域中声明的 f},之后重载解析能够无异议的决议 f(param) 调用中涉及的 f 就是当前作用域中声明的 f,即 void f(NS::T); 后者刚好又是 ::f 的再次声明,因此最终调用 ::f。

             来自: http://topic.csdn.net/u/20120112/20/586d5591-e889-4640-aae2-8e7733546568.html?seed=776923340&r=77548792#r_77548792

 

后来, 我又进行了如下测试:

#include "stdafx.h"
#include <iostream>
using namespace std;

namespace NS {
class T { };
void f(T)
{
cout <<"NS::f()" <<endl;
}
}

void f(NS::T)
{
cout <<"Global::f()" <<endl;
}

NS::T parm;

void bar()
{
void f(NS::T); // 由于 f 的声明和调用在同一函数体内, 所以不会引起 ADL. 如果把
// 此声明放在函数体外, 则会引发 ADL 导致 ambiguous.

f(parm); // 调用了 ::f.
}


int main()
{
// void f(NS::T); 放在这里就会引起 ambiguous问题.
bar();

}

最终的出的结论就是: 当函数声明和使用放在同一函数体内时, 则 ADL 不会向现有的重载函数集合中添加任何函数. 这也是避免了上述例子中没有发生 ambiguous 的原因!

posted @ 2012-02-14 15:38  walfud  阅读(249)  评论(0编辑  收藏  举报