c++11-17 模板核心知識(十四)—— 解析模板之依賴型模板名稱 Dependent Names of Templates(.template/->template/::template)
阿新 • • 發佈:2020-12-08
- [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