1. 程式人生 > >c++11-17 模板核心知識(十四)—— 解析模板之依賴型模板名稱 Dependent Names of Templates(.template/->template/::template)

c++11-17 模板核心知識(十四)—— 解析模板之依賴型模板名稱 Dependent Names of Templates(.template/->template/::template)

- [tokenization與parsing](#tokenization與parsing) - [解析模板之型別的依賴名稱 Dependent Names of Templates](#解析模板之型別的依賴名稱-dependent-names-of-templates) - [Example One](#example-one) - [Example Two](#example-two) - [Example Three](#example-three) 有時間的建議先看下上篇文章 : [c++11-17 模板核心知識(十三)—— 名稱查詢與ADL](https://github.com/zhangyachen/zhangyachen.github.io/issues/163) ## tokenization與parsing 絕大多數語言在編譯的時候都有兩個階段: * tokenization,或者叫scanning/lexing * parsing tokenization階段會讀取原始碼並生成一系列token. 例如:`int *p = 0;`,tokenizer會生成關鍵字int、運算子*、識別符號p、運算子=、整數0、運算子; 接下來,parser會遞迴的減少標記,尋找已知的模式。例如:token 0是一個合法的表示式,*p組合也是一個合法的宣告,它和後面的=0組合也是一個合法**初始化宣告**。最後,int是一個已知的型別,後面跟著**初始化宣告** : *p=0,所以,我們得到了一個初始化p的宣告 ## 解析模板之型別的依賴名稱 Dependent Names of Templates 關於模板解析有六個大方面: * 非模板中的上下文相關性 Context Sensitivity in Nontemplates * 依賴型型別名稱 Dependent Names of Types * 依賴型模板名稱 Dependent Names of Templates <----- * using-declaration中的依賴型名稱 Dependent Names in Using Declarations * ADL和顯式模板實參 ADL and Explicit Template Arguments * 依賴性表示式 Dependent Expressions 這篇文章先講下程式碼中比較常見的第三點 : 依賴型模板名稱(Dependent Names of Templates) **這裡有一個很重要的概念 :** 在[c++11-17 模板核心知識(十三)—— 名稱查詢與ADL](https://github.com/zhangyachen/zhangyachen.github.io/issues/163)中介紹過的Dependent Name:依賴於模板引數的名稱,也就是訪問運算子左面的表示式型別依賴於模板引數。例如:std::vector::iterator是一個 Dependent Name,但假如T是一個已知型別的別名(using T = int),那就不是Dependent Name。 通常而言, 編譯器會把模板名稱後面的<當做模板引數列表的開始,否則,<就是比較運算子。**但是,當引用的模板名稱是Dependent Name時,編譯器不會假定它是一個模板名稱,除非顯示的使用template關鍵字來指明**,模板程式碼中常見的`->template`、`.template`、`::template`就應用於這種場景中。 下面看幾個例子。 ### Example One ```c++ template void printBitset (std::bitset const& bs) { std::cout << bs.template to_string, std::allocator>(); } ``` 這裡,引數bs依賴於模板引數N。所以,我們必須通過template關鍵字讓編譯器知道bs是一個模板名稱,否則按照上面的規則,`<`會被當做比較符——小於號。 ### Example Two [The template keyword as qualifier (C++ only)](https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.cbclx01/keyword_template_qualifier.htm)中的例子: ```c++ #include using namespace std; class X { public: template struct S { void h() { cout << "member template's member function: " << j << endl; } }; template void f() { cout << "Primary: " << i << endl; } }; template<> void X::f<20>() { cout << "Specialized, non-type argument = 20" << endl; } template void g(T* p) { p->template f<100>(); p->template f<20>(); typename T::template S<40> s; // use of scope operator on a member template s.h(); } int main() { X temp; g(&temp); } ``` 這裡,引數p依賴模板引數T。注意`typename T::template S<40> s;`的使用。 ### Example Three ```c++ template class Shell { public: template class In { public: template class Deep { public: virtual void f(); }; }; }; template class Weird { public: void case1(typename Shell::template In::template Deep *p) { p->template Deep::f(); // inhibit virtual call } void case2(typename Shell::template In::template Deep &p) { p.template Deep::f(); // inhibit virtual call } }; ``` 引數p依賴模板引數T。編譯器不會去判斷`p.Deep`是不是模板。如果不指定template,那麼`p.Deep::f()`就會被解析成`((p.Deep)f();`,`<`被當做比較符。 基於上面的例子,我們也可以知道,`->template`、`.template`、`::template`只存在於模板中,並且是在Dependent Name的場景下使用(依賴於模板引數)。 (完) **朋友們可以關注下我的公眾號,獲得最及時的更新:** ![image](https://user-images.githubusercontent.com/14103319/92430320-11583200-f1c7-11ea-940d-1e297d2f3