九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項超值服

開通VIP
模板中的名字查找問題

問題起源

先看下面很簡單的一小段程序。

#include template typename T>struct Base { void fun() { std::cout <>'Base::fun' <>std::endl; }};template typename T>struct Derived : Base{ void gun() { std::cout <>'Derived::gun' <>std::endl; fun(); }};

這段代碼在 GCC 下很意外地編譯不過,原因竟然是找不到 fun 的定義,可是明明就定義在基類中了好嗎!為什么視而不見呢?顯然這和編譯器對名字的查找方式有關(guān),那這里面究竟有什么玄機(jī)呢?上述代碼是寫得不規(guī)范,還是 GCC 竟然存在這樣愚蠢而又莫名其妙的 bug?

C++ 標(biāo)準(zhǔn)的要求

對于模板中引用的符號,C++ 的標(biāo)準(zhǔn)有這樣的要求:

  1. 如果名字不依賴于模板中的模板參數(shù),則該符號必須定義在當(dāng)前模板可見的上下文內(nèi)。

  2. 如果名字是依賴于模板中的模板參數(shù),則該符號是在實例化該模板時,才對該符號進(jìn)行查找。

也就是說,對于前面提到的例子,gun() 函數(shù)中調(diào)用 fun(),由于該 fun() 并不依賴于 Derived 的模板參數(shù)T,因此在編譯器看來該調(diào)用就相當(dāng)于 ::fun(),直接把它當(dāng)成是一個外部的符號去查找,而此時外部又沒有定義該函數(shù),因此就報錯了。要去除這種錯誤,解決的方法很簡單,只要在調(diào)用 fun 的地方,人為地加上該調(diào)用對模板參數(shù)的依賴則可。

template typename T>struct Derived : Base{ void gun() { std::cout <>'Derived::gun' <>std::endl; this->fun();// or Base::fun(); }};

加上 this 之后,fun 就依賴于當(dāng)前 Derived 類,也就間接依賴了模板參數(shù)T,因此名字的查找就會被推遲到該類被實例化時才去基類中查找。

兩階段名字查找

從前面的介紹,我們可以看到編譯器對模板中引用的符號的查找是分為兩個階段的:

  1. 符號不依賴于當(dāng)前模板參數(shù),該符號則被當(dāng)作是外部的符號,直接在當(dāng)前模板所在的域中去查找。

  2. 符號如依賴于當(dāng)前模板參數(shù),則對該符號的查找被推遲到模板被實例化時。

為什么要這樣區(qū)別對待呢?原因其實很簡單,編譯器在看到模板 Derived 的定義時,還不能確定它的基類最后是怎樣的:Base 有可能會在后面被特化,使得最后被繼承的具體基類中不一定還有 fun() 函數(shù)。

template <>struct Baseint> { void fun2() { std::cout <>'Specialized, Base::fun2' <>std::endl; }};

因此編譯器在看到模板類的定義時,還不能判斷它的基類最后會被實例化成怎樣,所以對依賴于模板參數(shù)的符號的查找只能推遲到該模板被實例化時才進(jìn)行,而如果符號不依賴于模板參數(shù),顯然沒有這個限制,因此可以在看到模板的定義時就直接進(jìn)行查找,于是就出現(xiàn)了對不同符號的兩階段查找。

符號與類型問題

對于前面介紹中提到的符號,我們其實默認(rèn)指的是變量,細(xì)心的讀者可能會想到,在繼承類中引用的符號,還可能會是類型,而由于模板特化的存在,在名字查找的第一階段編譯器也是沒法判斷出該符號最后到底是怎樣的類型,甚至不能知道是不是一個類型。

template typename T>struct Base { typedef char* baseT;};template typename T>struct Derived : Base{ void gun() { Base::baseT p = 'abc'; }};
template <>struct Baseint>{ typedef int baseT;};template <>struct Basefloat>{ int baseT;};

如上例子,Derived 中 gun() 函數(shù)對 Base::baseT 的引用會造成編譯器的迷惑,它在看到 Derided 的定義時,根本無從知道 Base::baseT 究竟是一個變量名,還是一個類型,以及什么類型?而它又不能直接把這一部分相關(guān)的代碼全部都推遲到第二階段再進(jìn)行,因此在這兒它就會報錯了:它可以不知道這個類型最后是什么類型,但它必須知道它究竟是不是類型,如果連這個都不知道,接下來相關(guān)的代碼它都沒法去解析了。因此,實際上,編譯器在看到一個與模板參數(shù)相關(guān)的符號時,默認(rèn)它都是當(dāng)作一個變量來處理的,所以在上述的例子中,編譯器在看到 Derived 的定義時,它直接把 Base::baseT 當(dāng)成了一個變量來處理,所以就會報錯了。

那么,我們要怎樣才能讓編譯器知道其實 Base::baseT 是一個類型呢? 必須得顯式地告訴它,因此需要在引用 Base::baseT 時,顯式地加入一個關(guān)鍵字:typename.

template typename T>struct Derived : Base{ void gun() { typename Base::baseT p = 'abc'; }};

此時,編譯器看到有 typename 顯式地指明 baseT 是一個類型,它就不會再把它默認(rèn)當(dāng)成是一個變量了,從而使得名字查找的第一個階段可以繼續(xù)下去。

總結(jié)

模板中名字的查找會因為該名字是否依賴于模板參數(shù)而有所不同。

依賴于模板參數(shù)的名字(如函數(shù)的參數(shù)的類型是模板的參數(shù)),其符號解析會在第二階段進(jìn)行,其查找方式有兩個:

  1. 在模板定義的域內(nèi)可見的符號。(很嚴(yán)格)
  2. 在實例化模板的域內(nèi)通過 ADL 的方式查找符號。(也很嚴(yán)格,杜絕了不同 namespace 內(nèi)部重復(fù)定義導(dǎo)致沖突的問題)。

而不依賴于模板參數(shù)的符號,則只會在定義模板的可見域內(nèi)進(jìn)行查找,語言的定義嚴(yán)格如上所述,但實際編譯器的支持上,msvc 不支持兩階段的查找(vc 2010 以前),gcc 的實現(xiàn)在 4.7 以前也不完全符合標(biāo)準(zhǔn),一個比較全面的符合規(guī)范的例子,請參看如下:

void f(char); // 第一個 f 函數(shù) templateclass T> void g(T t) { f(1); // 不依賴參數(shù)的符號,符號解釋在第一階段進(jìn)行,找到 ::f(char) f(T(1)); // 依賴參數(shù)的符號: 查找推遲 f(t); // 依賴參數(shù)的符號: 查找推遲} enum E { e };void f(E); // 第二個 f 函數(shù)void f(int); // 第三個 f 函數(shù) void h() { g(32); // 實例化 g, 此時進(jìn)行查找 f(T(1)) 和 f(t) // f(t) 的查找找到 f(char),因為是通過非 ADL 方式查找的(T 是 int,ADL 失效),而定義模板的域內(nèi)只有 f(char)。 // 同理,f(T(1)) 的查找也只找到 f(char)。 g(e); // 實例化 g, 此時進(jìn)行查找 f(T(1)) 和 f(t),因為參數(shù)都是用戶定義的類型,ADL 起效,因此兩者均找到了 f(E),} typedef double A;templateclass T> class B { typedef int A;};templateclass T> struct X : B { A a; // 此處 A 為 double};

【引用】

http://gcc.gnu.org/onlinedocs/gcc/Name-lookup.html
http://womble.decadent.org.uk/c++/template-faq.html
http://en.cppreference.com/w/cpp/language/unqualified_lookup
https://gcc.gnu.org/gcc-4.7/porting_to.html
http://en.cppreference.com/w/cpp/language/adl


本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
C++語言的15個晦澀特性
<轉(zhuǎn)載>獨一無二的C++模板
C模板沉思錄(上)
SFINAE
認(rèn)識模板元編程
編譯期的斷言(Assertion)
更多類似文章 >>
生活服務(wù)
熱點新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服