柯尼希查找, or 参数相关查找 http://en.wikipedia.org/wiki/Argument-dependent_name_lookup,描述了 C++ 编译器如何查找非限定名称。
C++11 标准 § 3.4.2/1 规定:
当函数调用(5.2.2)中的后缀表达式是非限定 ID 时,可以搜索在通常的非限定查找(3.4.1)期间未考虑的其他命名空间,并且在这些命名空间中,命名空间范围的友元函数声明( 11.3) 可能会发现其他不可见的情况。对搜索的这些修改取决于参数的类型(对于模板模板参数,模板的命名空间
争论)。
In simpler terms Nicolai Josuttis states1:
如果在函数的命名空间中定义了一个或多个参数类型,则不必限定函数的命名空间。
一个简单的代码示例:
namespace MyNamespace
{
class MyClass {};
void doSomething(MyClass) {}
}
MyNamespace::MyClass obj; // global object
int main()
{
doSomething(obj); // Works Fine - MyNamespace::doSomething() is called.
}
在上面的例子中,既没有using
-声明也不是using
-directive 但编译器仍然正确识别非限定名称doSomething()
作为命名空间中声明的函数MyNamespace
通过应用柯尼希查找.
它是如何工作的?
该算法告诉编译器不仅要查看本地范围,还要查看包含参数类型的命名空间。因此,在上面的代码中,编译器发现该对象obj
,这是函数的参数doSomething()
, 属于命名空间MyNamespace
。因此,它会查看该名称空间来找到以下声明doSomething()
.
Koenig 查找的优点是什么?
正如上面的简单代码示例所示,Koenig 查找为程序员提供了方便和易用性。如果没有 Koenig 查找,程序员就会产生开销,需要重复指定完全限定的名称,或者使用大量的名称。using
- 声明。
为什么 Koenig 查找受到批评?
过度依赖 Koenig 查找可能会导致语义问题,有时会让程序员措手不及。
考虑以下示例std::swap http://en.cppreference.com/w/cpp/algorithm/swap,这是交换两个值的标准库算法。对于 Koenig 查找,使用该算法时必须谨慎,因为:
std::swap(obj1,obj2);
可能不会表现出与以下内容相同的行为:
using std::swap;
swap(obj1, obj2);
使用 ADL,哪个版本swap
函数的调用取决于传递给它的参数的名称空间。
如果存在命名空间A
, 而如果A::obj1
, A::obj2
, and A::swap()
存在,那么第二个示例将导致调用A::swap()
,这可能不是用户想要的。
此外,如果由于某种原因两者A::swap(A::MyClass&, A::MyClass&)
and std::swap(A::MyClass&, A::MyClass&)
已定义,那么第一个示例将调用std::swap(A::MyClass&, A::MyClass&)
但第二个不会编译,因为swap(obj1, obj2)
会很含糊。
Trivia:
为什么称为“Koenig 查找”?
因为它是由前 AT&T 和贝尔实验室研究员和程序员设计的,安德鲁·科尼格 http://en.wikipedia.org/wiki/Andrew_Koenig_%28programmer%29.
进一步阅读:
**1** The definition of Koenig lookup is as defined in Josuttis' book, *The C++ Standard Library: A Tutorial and Reference*.