久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

模板中的名字查找問題

 wangcqqj123 2017-02-19

問題起源

先看下面很簡(jiǎn)單的一小段程序,。

#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 的定義,,可是明明就定義在基類中了好嗎,!為什么視而不見呢?顯然這和編譯器對(duì)名字的查找方式有關(guān),,那這里面究竟有什么玄機(jī)呢,?上述代碼是寫得不規(guī)范,還是 GCC 竟然存在這樣愚蠢而又莫名其妙的 bug,?

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

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

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

  2. 如果名字是依賴于模板中的模板參數(shù),,則該符號(hào)是在實(shí)例化該模板時(shí),,才對(duì)該符號(hào)進(jì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{ void gun() { std::cout << 'Derived::gun' << std::endl; this->fun();// or Base::fun(); }};

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

兩階段名字查找

從前面的介紹,,我們可以看到編譯器對(duì)模板中引用的符號(hào)的查找是分為兩個(gè)階段的:

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

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

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

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{ void gun() { Base::baseT p = 'abc'; }};
template <>struct Base<int>{ typedef int baseT;};template <>struct Base<float>{ int baseT;};

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

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

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

此時(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è):

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

而不依賴于模板參數(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, 此時(shí)進(jìn)行查找 f(T(1)) 和 f(t) // f(t) 的查找找到 f(char),因?yàn)槭峭ㄟ^非 ADL 方式查找的(T 是 int,,ADL 失效),,而定義模板的域內(nèi)只有 f(char)。 // 同理,,f(T(1)) 的查找也只找到 f(char),。 g(e); // 實(shí)例化 g, 此時(shí)進(jìn)行查找 f(T(1)) 和 f(t),因?yàn)閰?shù)都是用戶定義的類型,,ADL 起效,,因此兩者均找到了 f(E),} typedef double A;template<class T> class B { typedef int A;};template<class T> struct X : B { A a; // 此處 A 為 double};

【引用】

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


    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多