You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
externint count; // #1intlookup_example(int count) // #2
{
if (count < 0) {
int count = 1; // #3lookup_example(count); // unqualified count refers to #3
}
return count + ::count; // the first (unqualified) count refers to #2 ;
} // the second (qualified) count refers to #1
这个例子也很符合直觉。
但是下面这个例子就没那么正常:
template<typename T>
T max (T a, T b) {
return b < a ? a : b;
}
namespaceBigMath {
classBigNumber {
...
};
booloperator < (BigNumber const&, BigNumber const&);
...
}
using BigMath::BigNumber;
voidg (BigNumber const& a, BigNumber const& b) {
...
BigNumber x = ::max(a,b);
...
}
#include<iostream>intmain() {
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 applyendl(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
}
在C++中,如果编译器遇到一个名称,它会寻找这个名称代表什么。比如x*y,如果x和y是变量的名称,那么就是乘法。如果x是一个类型的名称,那么就声明了一个指针。
C++是一个
context-sensitive
的语言 : 必须知道上下文才能知道表达式的意义。那么这个和模板的关系是什么呢?构造一个模板必须知道几个上下文:名称分类
引入两个重要的概念:
::
、->
或者.
。this->count
就是一个qualified name,但count不是,因为它的作用域没有被显示的指明,即使它和this->count
是等价的。名称查找
名称查找有很多细节,这里我们只关注几个主要的点。
ordinary lookup
对于qualified name来说,会有显示指明的作用域。如果作用域是一个类,那么基类也会被考虑在内,但是类的外围作用域不会被考虑:
这点很符合直觉。
相反,对于非qualified name来说,会在外围作用域逐层查找(假如在类成员函数中,会先找本类和基类的作用域)。这叫做ordinary lookup :
这个例子也很符合直觉。
但是下面这个例子就没那么正常:
这里的问题是:当调用max时,
ordinary lookup
不会找到BigNumber的operator <
。如果没有一些特殊规则,那么在C++ namespace场景中,会极大的限制模板的适应性。ADL就是这个特殊规则,用来解决此类的问题。ADL (Argument-Dependent Lookup)
ADL出现在C++98/C++03中,也被叫做Koenig lookup,应用在非qualified name上(下文简称unqualified name)。在函数调用表达式中(f(a1, a2, a3, ... ),包含隐式的调用重载operator,例如 << ),ADL应用一系列的规则来查找
unqualified function names
。ADL会将函数表达式中实参的
associated namespaces
和associated classes
加入到查找范围,这也就是为什么叫Argument-Dependent Lookup. 例如:某一类型是指向class X的指针,那么它的associated namespaces
和associated classes
会包含X和X所属的任何class和namespace.对于给定的类型,
associated classes
和associated namespaces
按照一定的规则来定义,大家可以看下官网Argument-dependent lookup,实在有点多,不写在这里了。理解为什么需要ADL、什么时候应用到ADL时,按照对应的场景再去查就行~额外需要注意的一点是,ADL会忽略using :
namespace N
中的using namespace X
会被ADL忽略,所以在main函数中,X::f()不会被考虑。官网的例子
看下官网的例子帮助理解:
注意最后一点
(endl)(std::cout);
,如果函数的名字被括号包起来了,那也不会应用ADL。再来一个:
这个比较好理解,不解释了。
ADL的缺点
依赖ADL有可能会导致语义问题,这也是为什么有的时候需要在函数前面加
::
,或者一般推荐使用xxx::func,而不是using namespace xxx 。因为前者是qualified name,没有ADL的过程。引用现代C++之ADL中的例子,只看swap就行,类的其他函数可以略过:
(完)
朋友们可以关注下我的公众号,获得最及时的更新:
The text was updated successfully, but these errors were encountered: