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,而不是編譯出錯。