有時間的建議先看下上篇文章 : c++11-17 模板核心知識(十三)—— 名稱查詢與ADL
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中介紹過的Dependent Name:依賴於模板引數的名稱,也就是訪問運算子左面的表示式型別依賴於模板引數。例如:std::vector
通常而言, 編譯器會把模板名稱後面的<當做模板引數列表的開始,否則,<就是比較運算子。但是,當引用的模板名稱是Dependent Name時,編譯器不會假定它是一個模板名稱,除非顯示的使用template關鍵字來指明,模板程式碼中常見的->template
、.template
、::template
就應用於這種場景中。
下面看幾個例子。
Example One
template<unsigned long N>
void printBitset (std::bitset<N> const& bs) {
std::cout << bs.template to_string<char, std::char_traits<char>, std::allocator<char>>();
}
這裡,引數bs依賴於模板引數N。所以,我們必須通過template關鍵字讓編譯器知道bs是一個模板名稱,否則按照上面的規則,<
會被當做比較符——小於號。
Example Two
The template keyword as qualifier (C++ only)中的例子:
#include <iostream>
using namespace std;
class X {
public:
template <int j> struct S {
void h() {
cout << "member template's member function: " << j << endl;
}
};
template <int i> void f() {
cout << "Primary: " << i << endl;
}
};
template<> void X::f<20>() {
cout << "Specialized, non-type argument = 20" << endl;
}
template<class T> 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
template <typename T> class Shell {
public:
template <int N> class In {
public:
template <int M> class Deep {
public:
virtual void f();
};
};
};
template <typename T, int N> class Weird {
public:
void case1(typename Shell<T>::template In<N>::template Deep<N> *p) {
p->template Deep<N>::f(); // inhibit virtual call
}
void case2(typename Shell<T>::template In<N>::template Deep<N> &p) {
p.template Deep<N>::f(); // inhibit virtual call
}
};
引數p依賴模板引數T。編譯器不會去判斷p.Deep
是不是模板。如果不指定template,那麼p.Deep<N>::f()
就會被解析成((p.Deep)<N)>f();
,<
被當做比較符。
基於上面的例子,我們也可以知道,->template
、.template
、::template
只存在於模板中,並且是在Dependent Name的場景下使用(依賴於模板引數)。
(完)
朋友們可以關注下我的公眾號,獲得最及時的更新: