依赖于参数的查找
依赖于参数的查找 (ADL),也称为 Koenig 查找[1],是一组用于查找函数调用表达式中未限定的函数名的规则,包括对重载运算符的隐式函数调用。除了通常的未限定名称查找中考虑的范围和命名空间之外,还会在其参数的命名空间中查找这些函数名。
依赖于参数的查找使得在不同的命名空间中使用运算符成为可能。示例
#include <iostream> int main() { std::cout << "Test\n"; // There is no operator<< in global namespace, but ADL // examines std namespace because the left argument is in // std and finds std::operator<<(std::ostream&, const char*) operator<<(std::cout, "Test\n"); // Same, using function call notation // However, std::cout << endl; // Error: “endl” is not declared in this namespace. // This is not a function call to endl(), so ADL does not apply endl(std::cout); // OK: this is a function call: ADL examines std namespace // because the argument of endl is in std, and finds std::endl (endl)(std::cout); // Error: “endl” is not declared in this namespace. // The sub-expression (endl) is not an unqualified-id }
内容 |
[编辑] 详细信息
首先,如果通常的未限定名称查找产生的查找集中包含以下任何一项,则不考虑依赖于参数的查找
否则,对于函数调用表达式中的每个参数,都会检查其类型以确定与其关联的命名空间和类集,这些命名空间和类将被添加到查找中。
T
的指针或指向 T
数组的指针的参数,会检查类型 T
并将其关联的类和命名空间集添加到该集中。X
的成员函数 F
的指针的参数,会检查函数参数类型、函数返回类型和类 X
,并将它们关联的类和命名空间集添加到该集中。X
的数据成员 T
的指针的参数,会检查成员类型和类型 X
,并将它们关联的类和命名空间集添加到该集中。- 此外,如果重载集是由 模板标识符 命名,则会检查其所有类型模板参数和模板模板参数(但不会检查非类型模板参数),并将与其关联的类和命名空间集添加到该集合中。
如果关联的类和命名空间集中存在任何命名空间为 内联命名空间,则也会将其封闭的命名空间添加到该集合中。 如果关联的类和命名空间集中存在任何命名空间直接包含内联命名空间,则会将该内联命名空间添加到该集合中。 |
(自 C++11 起) |
在确定关联的类和命名空间集之后,为了进行进一步的 ADL 处理,将丢弃此集中所有类中找到的声明,但第 2 点中提到的命名空间范围的友元函数和函数模板除外。
通过普通的 非限定查找 找到的声明集和通过 ADL 在关联集的所有元素中找到的声明集将合并,并遵循以下特殊规则
[编辑] 注意
由于参数依赖查找,与类位于同一命名空间中的非成员函数和非成员运算符被视为该类的公共接口的一部分(如果它们通过 ADL 找到)[2].
ADL 是通用代码中用于交换两个对象的既定惯例背后的原因:using std::swap; swap(obj1, obj2);,因为直接调用 std::swap(obj1, obj2) 不会考虑可能与 obj1 或 obj2 类型位于同一命名空间中的用户定义的 swap()
函数,而仅仅调用非限定的 swap(obj1, obj2) 如果没有提供用户定义的重载,则什么也不会调用。特别是,std::iter_swap 和所有其他标准库算法在处理 可交换 类型时使用这种方法。
名称查找规则使得在全局命名空间或用户定义的命名空间中声明操作符来操作来自 std
命名空间的类型变得不切实际,例如,operator>> 或 operator+ 用于 std::vector 或 std::pair(除非向量/对的元素类型是用户定义的类型,这会将它们的命名空间添加到 ADL)。这些操作符不会从模板实例化中查找,例如标准库算法。有关更多详细信息,请参阅 依赖名称。
ADL 可以找到一个 友元函数(通常是重载运算符),该函数完全定义在类或类模板内,即使它从未在命名空间级别声明过。
template<typename T> struct number { number(int); friend number gcd(number x, number y) { return 0; }; // Definition within // a class template }; // Unless a matching declaration is provided gcd is // an invisible (except through ADL) member of this namespace void g() { number<double> a(3), b(4); a = gcd(a, b); // Finds gcd because number<double> is an associated class, // making gcd visible in its namespace (global scope) // b = gcd(3, 4); // Error; gcd is not visible }
尽管即使普通查找没有找到任何内容,函数调用也能通过 ADL 解析,但对具有显式指定模板参数的 函数模板 的函数调用要求通过普通查找找到模板的声明(否则,遇到未知名称后跟小于号就是一个语法错误)。 namespace N1 { struct S {}; template<int X> void f(S); } namespace N2 { template<class T> void f(T t); } void g(N1::S s) { f<3>(s); // Syntax error until C++20 (unqualified lookup finds no f) N1::f<3>(s); // OK, qualified lookup finds the template 'f' N2::f<3>(s); // Error: N2::f does not take a non-type parameter // N1::f is not looked up because ADL only works // with unqualified names using N2::f; f<3>(s); // OK: Unqualified lookup now finds N2::f // then ADL kicks in because this name is unqualified // and finds N1::f } |
(在 C++20 之前) |
在以下上下文中,仅执行 ADL 查找(即,仅在关联命名空间中查找)
|
(自 C++11 起) |
- 模板实例化点处的 依赖名称查找。
|
(自 C++17 起) |
[编辑] 示例
本节内容不完整 原因:更多示例 |
来自 http://www.gotw.ca/gotw/030.htm 的示例
namespace A { struct X; struct Y; void f(int); void g(X); } namespace B { void f(int i) { f(i); // Calls B::f (endless recursion) } void g(A::X x) { g(x); // Error: ambiguous between B::g (ordinary lookup) // and A::g (argument-dependent lookup) } void h(A::Y y) { h(y); // Calls B::h (endless recursion): ADL examines the A namespace // but finds no A::h, so only B::h from ordinary lookup is used } }
[编辑] 缺陷报告
以下行为更改缺陷报告被追溯应用到先前发布的 C++ 标准。
DR | 应用于 | 已发布的行为 | 正确行为 |
---|---|---|---|
CWG 33 | C++98 | 关联的命名空间或类未指定 如果用于查找的参数是 一组重载函数或函数模板的地址 |
已指定 |
CWG 90 | C++98 | 嵌套非联合类的关联类 不包括其封闭类,但嵌套 联合与封闭类相关联 |
非联合也相关联 |
CWG 239 | C++98 | 在普通 非限定查找中找到的块范围函数声明没有阻止 ADL 发生 |
ADL 未被考虑,除了 using 声明 |
CWG 997 | C++98 | 依赖参数类型和返回类型被 排除在确定函数模板的关联 类和命名空间之外 |
已包含 |
CWG 1690 | C++98 C++11 |
ADL 无法找到返回的 lambda(C++11)或 局部类类型对象(C++98) |
可以找到它们 |
CWG 1691 | C++11 | ADL 对不透明枚举声明具有意外行为 | 已修复 |
CWG 1692 | C++98 | 双重嵌套类没有关联命名空间 (它们的封闭类不是任何命名空间的成员) |
关联命名空间被 扩展到最内层的 封闭命名空间 |
CWG 2857 | C++98 | 不完整 类类型的关联类包括其基类 |
不包含 |
[编辑] 另请参阅
[编辑] 外部链接
|