編譯器的詞法分析和語法分析是如何處理C++的模板?

不考慮可變模板參數,不考慮模板的模板參數,不考慮偏特化,僅考慮模板的類型參數和非類型參數.是不是用LL解析器或者LR解析器就可以成功解析出包含這種模板子集的AST?


你的這個問題實在太大了,建議再縮小一下,編譯器解析模板我覺得我在我的博客完全可以寫個十幾篇的連載。

我這裡就說一個小方面吧,就是模板的實例化的選取,即編譯器與鏈接器如何確保在執行的時候只有一份模板實例,因為我們知道編譯器遇到模板實例化的時候就會實例化一份(而還有模板的特化順序處理,Candidate選取等這裡都不講,所以我說你這個問題實在太大太大了),而講這個問題是可以引導出C++11用於提高Compiler Performance的extern template新特性(注意不是export)。那麼我這裡簡單提一下IBM的XL C++編譯器如何處理模板的(我想其它編譯器應該也有類似的思路),模板和普通的程序在IBM XL C++編譯器中走的是兩條道路,即他們不是在一條道路上的。遇到模板的時候,編譯器都會「壓制」到後面來處理。而IBM XL C++ Compiler為了完成模板實例化選取,然後保證鏈接器只會拿一份實例,對模板採取了一個特殊的處理,就是使用了weak symbols. 舉一個簡單的例子吧

// a.h
template & int f(T){}
// 1.cpp
#include "a.h"
int fun1()
{
f(1);
}
// 2.cpp
#include "a.h"
int fun2()
{
f(2);
}

我們這裡在1.cpp會生成一份f&(int)的實例,而在2.cpp也同樣會生成一份。那麼我們鏈接器鏈接1.o和2.o的時候,就會出現兩份f&(int)的實例,這是不允許的。那麼為了達成允許這樣的情況,編譯器就使用了weak symbols這個東西,那麼有了這個東西就允許這樣的事情存在了,而有了weak,那麼自然也會有strong symbols了,那麼全局變數這樣的東西就是strong的。OK,有了weak symbols,那麼就會提醒鏈接器隨意取一份實例,這在大部分編譯器中都是取的第一份,如IBM XL C++ for Linux,而在給出的文檔中,聲明的是隨機取一份,但是我可以說的是其實就是第一份,這在IBM XL C++ for AIX也是如此,GCC也如此,但是你不能就假設一直都是了。

接下來,我想講的是C++11的extern template。這個特性其實對於編譯器性能也是一個提高。如上例所述,我們在1.cpp遇到f(1)會實例化一份f&(int),而在2.cpp遇到f(2)也會實例化一份。然而我們知道,鏈接器只會拿一份,其餘的都全被丟掉了,如果我們有大量的這種實例化,就會產生非常冗餘的代碼,也會加長編譯的時間,那麼C++11就引入了一個新特性叫做extern template.

// a.h
template & int f(T){}
extern template int f&(int);
// 1.cpp
#include "a.h"
template int f&(int);
int fun1(){
f(1);
}
// 2.cpp
#include "a.h"
int fun2(){
f(2);
}

現在的情況就是在a.h聲明了模板,而在1.cpp顯示實例化了f&(int),而在2.cpp的時候,編譯器就會假定在其它的編譯單元已經存在了f&(int)了,那麼就不會再顯示實例化一份f&(int)了。這樣的話,如果你有一類模板實例化非常頻繁的代碼的話,通過這樣的辦法,可以有效的降低編譯與鏈接時間,以及產生的冗餘代碼和目標文件的大小。

其實我們回過頭看這個特性,其實思想和以前的extern是類似的,只是這次遷移到了模板而已。那麼這個特性有沒有一些注意項呢?我認為有如下幾點。

1. 該特性適用於函數模板和類模板

2. 如果你先聲明了extern template,卻沒有顯示實例化,那麼會報鏈接錯誤

3. 不能用於靜態函數模板(這個是比較顯而易見的)

4. C++11沒有聲明是否可以用於inline,這是未定義的行為

5,.不應過度依賴此特性,應該將這個特性用在實例化頻繁的地方。

我好像針對你的提問,只講了一個非常非常小的地方,但是說了這麼多,好像又什麼沒有講一般,還是因為C++模板太複雜了。


推薦閱讀:

你對基於機器學習的編譯技術有什麼看法?
為什麼C++預處理期編譯期的支持比較薄弱?
想用用flex和bison寫個C的編譯器,應該如何處理C的宏?
peg, ometa 解決什麼問題,ometa-js怎麼入門/正確理解和認知?
(發散性問題,請隨意加上假設)一門編程語言同時擁有解釋器和編譯器的必要性有多大?

TAG:C | 編譯原理 | 編譯器 | 模板C |