Argument-dependent lookup
参数相关查找
参数相关的查找,也称为adl,或kenig查找,是用于查找不限定函数名的一组规则。函数调用表达式,包括隐式函数调用过载运算符这些函数名除了通常所考虑的范围和命名空间之外,还会在其参数的命名空间中查找。非限定名查找...
依赖于参数的查找可以使用在不同名称空间中定义的运算符。例子:
二次
#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 a function call expression
}
二次
细节
首先,如果通常生成的查找集是由参数相关的查找,则不考虑该查找。不合格查找包含下列任一内容:
1%29班级成员的声明
2%29在块作用域%28上的函数声明(%27 s不是使用-声明%29
3%29不是函数或函数模板%28的任何声明。一个函数对象或另一个变量,其名称与查找%27的函数的名称相冲突
否则,将检查函数调用表达式中的每个参数的类型,以确定关联的命名空间和类集
它将添加到查找中。
1%29对于基本类型的参数,相关的命名空间和类集为空。
2%29对于类类型%28(包括联合%29)的参数,集合由
A%29,类本身
B%29它的所有直接和间接基类
C%29如果类是另一类成员,它是其中一个成员的类。
D%29添加到集合中的类中最内部的封闭命名空间
类型为类模板专门化,除了类规则之外,还将检查以下类型,并将它们的关联类和命名空间添加到集合中
a%29类型模板参数提供的所有模板参数类型%28跳过非类型模板参数和跳过模板参数%29
B%29任何模板模板参数都是成员的命名空间
C%29任何模板模板参数都是成员的类(如果它们恰好是类成员模板%29)
4%29对于枚举类型的参数,将定义枚举的命名空间添加到集合中。如果枚举类型是类的成员,则将该类添加到集合中。
5%29对于指向T的类型指针或指向T数组的指针的参数,检查类型T并将其关联的类和命名空间集添加到集合中。
6%29对于函数类型的参数,检查函数参数类型和函数返回类型,并将它们相关联的类和命名空间集添加到集合中。
7%29对于指向类X的成员函数F的类型指针的参数,检查函数参数类型、函数返回类型和类X,并将它们相关联的类和命名空间集添加到集合中。
8%29对于指向类X的数据成员T的类型指针的参数,将检查成员类型和类型X,并将它们相关联的类和命名空间集添加到集合中。
9%29如果参数是名称或一组重载函数的地址表达式%28或函数模板%29,将检查重载集中的每个函数,并将其关联的类和命名空间集添加到集合中。
另外,如果用模板id%28模板名称命名的重载集为模板参数%29,则将检查其所有类型模板参数和模板参数%28(但不是非类型模板参数%29),并将其相关的类和命名空间集添加到集合中。
如果关联的类和命名空间集合中的任何命名空间都是内联命名空间,它的封闭命名空间也会添加到集合中。
如果关联的类和命名空间集合中的任何名称空间直接包含内联名称空间,则将内联命名空间添加到集合中。
在确定了关联的类和命名空间集之后,除命名空间作用域的朋友函数和函数模板外,该集合类中的所有声明都会被丢弃,以便进一步进行ADL处理,如下面第2点所述。
普通的声明集不合格查找并将ADL生成的关联集的所有元素中的声明集合合并,并使用以下特殊规则进行合并。
1%29使用-指令在关联的命名空间中被忽略。
在关联类中声明的2%29命名空间作用域朋友函数%28和函数模板%29通过adl可见,即使它们通过普通查找不可见。
3%29除函数和函数模板以外的所有名称都被忽略%28no与变量%29冲突
注记
由于与参数相关的查找,在类中定义的非成员函数和非成员运算符如果通过adl%29找到,则被视为该类%28的公共接口的一部分。[1].ADL是在通用代码中交换两个对象的成语背后的原因:
二次
using std::swap;
swap(obj1, obj2
二次
因为打电话std::swap
(obj1, obj2)
直接不考虑与obj 1或obj 2的类型相同的用户定义的交换区%28%29函数,而只调用不合格的swap(obj1, obj2)
如果没有提供用户定义的重载,就不会调用任何东西。特别是,std::iter_swap
和所有其他标准库算法在处理Swappable
类型。
名称查找规则使得在全局或用户定义的命名空间中声明操作自std命名空间的类型(例如自定义)中的运算符变得不切实际。operator>>或operator+为std::vector还是为了std::pair%28除非向量/对的元素类型是用户定义的类型,否则将它们的命名空间添加到ADL%29。这样的操作符不会从模板实例化中查找,比如标准库算法。见相依名称更多细节。
ADL可以找到一个朋友函数%28--通常是完全在类或类模板中定义的重载运算符%29,即使它从未在命名空间级别声明过。
二次
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解析,即使普通查找没有发现任何,但函数调用对功能模板对于显式指定的模板参数,需要有一个由普通查找%28找到的模板声明,否则,遇到一个未知的名称,后面跟着小于字符%29的语法错误。
二次
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 (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
}
二次
在下面的上下文中,ADL只查找%28,也就是说,在关联的命名空间中只查找%29:
- 非成员函数的查找
begin
和end
由范围-为如果成员查找失败,则循环
- 大相关名称查找从模板实例化的角度。
the lookup of non-member function get performed by structured binding declaration for tuple-like types | (since C++17) |
---|
- 非成员函数的查找
get
由结构化绑定声明用于元组类型。%28自C++17%29实例示例来自http://www.gotw.ca/gow/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 } }二次另见
- 名称查找
- 模板参数推导
- 过载分辨率
参考文献
- H.萨特%281998%29“班级中的%27s是多少?”。-接口原则“C++报告,10%283%29
© cppreference.com
在CreativeCommonsAttribution下授权-ShareAlike未移植许可v3.0。