問題起源先看下面很簡(jiǎn)單的一小段程序,。 #include 這段代碼在 GCC 下很意外地編譯不過,原因竟然是找不到 fun 的定義,,可是明明就定義在基類中了好嗎,!為什么視而不見呢?顯然這和編譯器對(duì)名字的查找方式有關(guān),,那這里面究竟有什么玄機(jī)呢,?上述代碼是寫得不規(guī)范,還是 GCC 竟然存在這樣愚蠢而又莫名其妙的 bug,? C++ 標(biāo)準(zhǔn)的要求對(duì)于模板中引用的符號(hào),,C++ 的標(biāo)準(zhǔn)有這樣的要求:
也就是說,,對(duì)于前面提到的例子,,gun() 函數(shù)中調(diào)用 fun(),,由于該 fun() 并不依賴于 Derived 的模板參數(shù)T,,因此在編譯器看來該調(diào)用就相當(dāng)于 ::fun(),,直接把它當(dāng)成是一個(gè)外部的符號(hào)去查找,,而此時(shí)外部又沒有定義該函數(shù),,因此就報(bào)錯(cuò)了,。要去除這種錯(cuò)誤,,解決的方法很簡(jiǎn)單,,只要在調(diào)用 fun 的地方,,人為地加上該調(diào)用對(duì)模板參數(shù)的依賴則可,。 template <typename T>struct Derived : Base 加上 this 之后,fun 就依賴于當(dāng)前 Derived 類,,也就間接依賴了模板參數(shù)T,,因此名字的查找就會(huì)被推遲到該類被實(shí)例化時(shí)才去基類中查找。 兩階段名字查找從前面的介紹,,我們可以看到編譯器對(duì)模板中引用的符號(hào)的查找是分為兩個(gè)階段的:
為什么要這樣區(qū)別對(duì)待呢,?原因其實(shí)很簡(jiǎn)單,,編譯器在看到模板 Derived 的定義時(shí),還不能確定它的基類最后是怎樣的:Base template <>struct Base<int> { void fun2() { std::cout << 'Specialized, Base::fun2' << std::endl; }}; 因此編譯器在看到模板類的定義時(shí),還不能判斷它的基類最后會(huì)被實(shí)例化成怎樣,,所以對(duì)依賴于模板參數(shù)的符號(hào)的查找只能推遲到該模板被實(shí)例化時(shí)才進(jìn)行,,而如果符號(hào)不依賴于模板參數(shù),顯然沒有這個(gè)限制,,因此可以在看到模板的定義時(shí)就直接進(jìn)行查找,,于是就出現(xiàn)了對(duì)不同符號(hào)的兩階段查找。 符號(hào)與類型問題對(duì)于前面介紹中提到的符號(hào),,我們其實(shí)默認(rèn)指的是變量,,細(xì)心的讀者可能會(huì)想到,在繼承類中引用的符號(hào),,還可能會(huì)是類型,,而由于模板特化的存在,,在名字查找的第一階段編譯器也是沒法判斷出該符號(hào)最后到底是怎樣的類型,甚至不能知道是不是一個(gè)類型,。 template <typename T>struct Base { typedef char* baseT;};template <typename T>struct Derived : Base template <>struct Base<int>{ typedef int baseT;};template <>struct Base<float>{ int baseT;}; 如上例子,,Derived 那么,,我們要怎樣才能讓編譯器知道其實(shí) Base template <typename T>struct Derived : Base 此時(shí),,編譯器看到有 typename 顯式地指明 baseT 是一個(gè)類型,它就不會(huì)再把它默認(rèn)當(dāng)成是一個(gè)變量了,,從而使得名字查找的第一個(gè)階段可以繼續(xù)下去,。 總結(jié)模板中名字的查找會(huì)因?yàn)樵撁质欠褚蕾囉谀0鍏?shù)而有所不同。 依賴于模板參數(shù)的名字(如函數(shù)的參數(shù)的類型是模板的參數(shù)),,其符號(hào)解析會(huì)在第二階段進(jìn)行,,其查找方式有兩個(gè):
而不依賴于模板參數(shù)的符號(hào),,則只會(huì)在定義模板的可見域內(nèi)進(jìn)行查找,,語言的定義嚴(yán)格如上所述,,但實(shí)際編譯器的支持上,msvc 不支持兩階段的查找(vc 2010 以前),,gcc 的實(shí)現(xiàn)在 4.7 以前也不完全符合標(biāo)準(zhǔn),,一個(gè)比較全面的符合規(guī)范的例子,請(qǐng)參看如下: void f(char); // 第一個(gè) f 函數(shù) template<class T> void g(T t) { f(1); // 不依賴參數(shù)的符號(hào),,符號(hào)解釋在第一階段進(jìn)行,,找到 ::f(char) f(T(1)); // 依賴參數(shù)的符號(hào): 查找推遲 f(t); // 依賴參數(shù)的符號(hào): 查找推遲} enum E { e };void f(E); // 第二個(gè) f 函數(shù)void f(int); // 第三個(gè) f 函數(shù) void h() { g(32); // 實(shí)例化 g 【引用】http://gcc./onlinedocs/gcc/Name-lookup.html |
|