1 第九章 模板中的名称
C++是一种上下文相关的语言:也就是说我们不能脱离他的上下文构造。
1.1 名称的分类
l 受限名称,如果前面有域解析运算符::,或者成员访问运算符(.或->),我们就称该名称为受限名称,比如this->count;
l 如果一个名称依赖于一个模板参数,我们就称他为依赖名称。比如 std::vector<T>::iterator,如果T是模板参数,则他是依赖名称,如果T是一个typedef,则他不是依赖名称。
1.2 名称查找
普通查找:在某个类内部定义的成员函数定义中,他会先查找该类和基类的作用域,然后才查找外围的作用域。这种查找也就是非受限名称的查找方式。
不过还应该添加一项:依赖于参数的查找(即ADL)。
1.3 ADL=argument-depentent lookup
在函数调用中,如果名称后面尖括号里面有实参表达式,那么ADL将会查找这些实参的associated class 和associated namespace。
ADL只能应用于非受限函数名称。这个非受限名称是指这个函数的名称是非受限的。
如果普通查找能够找到该名称,那么将不使用ADL。
我们来看下面的例子:
//9.2
template<typename T>
inline T const& max(T const& a, T const& b)
{
return a<b? b:a;
}
namespace BigMath
{
class BigNumber{
public:
BigNumber(int i):mem(i){};
private:
int mem;
public:
int GetMem() const{
return mem;
};
};
bool operator<(BigNumber const& a, BigNumber const& b)
{
return a.GetMem()<b.GetMem();
}
}
int _tmain(int argc, _TCHAR* argv[])
{
//9.2
BigMath::BigNumber bigNum1(2), bigNum2(3);
std::cout<<"max Nums is:"<<max(bigNum1, bigNum2).GetMem()<<std::endl;
return 0;
}
在上面的例子中,正常来说,我们应该看不到BinMath名字空间内的运算符 operator <,除非有特殊规则,那么这个特殊规则就是ADL。
再来看下面的例子:
namespace X{
template<typename T> void f(T)
{
std::cout<<"int template<typename T> void f(T)"<<std::endl;
}
}
namespace N
{
using namespace X;
enum E{ e1};
class TryUseTemplateF{};
void f(E){
std::cout<<"void f(E) in namespace N"<<std::endl;
};
}
void f(int i)
{
std::cout<<"void f(int) i="<<i<<std::endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
//9.2.1
//9.2.1
::f(N::e1); //1
f(N::e1); //2
N::TryUseTemplateF aObj;
f(aObj); //3
return 0;
}
编译器在编译其上的3时会报错。说是找不到对应的函数重载。
可见,在执行ADL的时候,名字空间N中的using directive会被忽略了。因为如果没有忽略的话,它就能够找到名字空间X内的模板函数f,而不是编译出错。