template<class T>
class A { public: friend void test(int x) { cout << 1; } private: T a; };
需求是,test函數想要成為A類的友元函數,來讀取A類中的private成員屬性,同時A類是一個泛型類,所以理所應當地想到這種寫法。在A類還沒有實例化對象的時候,這種寫法沒有任何報錯,當A類創建對象的時候,出現了奇怪的bug:
int main() { A<int> x; A<long long> y; test(1); cout << "沒有問題" << endl; }
A類創建了兩個對象,A類是泛型類,創建了一個int型的類,與一個long long型的類,結果出現了這個的報錯:

redefinition,說明void test重定義了,但是為什么?還有更詭異的現象:當x,與y同時存在的時候,就會報這樣的錯,但是當x,y只存在一個的時候,也就是A類只創建一種類型的對象時,它并不會報錯,可以正常運行。當時也沒有深究這個問題,但到了現在,隨著本人對靜態多態的理解加深,這個問題可以得到解答。
首先,先來了解一下c++的泛型:template的實現原理,簡單總結起來,就是五個字:自動生成代碼。
之所以泛型會被稱為靜態多態,是因為它并不是運行時來進行,而是在編譯期進行的。假如定義了一個泛型函數test:
void test(T t1, T t2) {
//pass
};
在沒有對它實體化之前,它只是一個模板,而不是一個函數,編譯器就可以認為沒有test這個函數。但是當使用到它的時候,編譯器會根據test函數的使用情況自動地生成相應的參數的函數,比如下面,main函數中使用到了double參數類型的test與int參數類型的test:
int main() {
double x1 = 0, x2 = 0;
int y1, y2;
test(x1 = 0, x2 = 0);
test(y1 = 0, y2 = 0);
return 0;
}
編譯器會檢測到你一共使用了int與double兩種參數的test,所以自動生成一個如下代碼:
void test(int t1, int t2) { //pass }; void test(double t1, double t2) { //pass };
這些事本來是由程序員自己來完成的,用template就可以偷懶,給你一個模板,讓編譯器自己生成代碼。這也是為什么,c++把它叫模板。因為template的函數或是類本身就不是一個函數或者類,它只是一個模板,告訴編譯器該怎么生成代碼,它本質上就是一種宏的替換,把template<class T>中的T替換成程序中所使用的類型,而這些操作就是在編譯期時完成的,而不是在運行時進行的,因為相應的代碼已經生成好了,不會有運行時開銷,所以稱為“靜態多態”。
這樣做當然會有缺點,就是程序的編譯時間會變長,生成的可執行文件會變大,但是用編譯時間去換運行時間是很劃算的,因為編譯只需要編譯一次,而一個程序要被運行很多次。
再說回之前的那個問題,知道了template的原理之后,之前的問題也就行容易理解了:在使用了A<int> x;的時候,編譯器檢測到了使用了int,所以生成了如下代碼:
class A { public: friend void test(int x) { cout << 1; } private: int a; //將template<class T>中的T替換成int. };
然后又檢測到了使用了A<long long> y;所以又生成了如下代碼:
class A { public: friend void test(int x) { cout << 1; } private: long long a; //將template<class T>中的T替換成long long. };
發現問題了嗎?void test(int x)被定義了兩次,成員函數是可以定義兩次的,因為A<int>與A<long long>是兩個不同的類,它們有一個名字相同的成員函數當然沒有問題,但是友元函數不同,友元函數并不屬于這個類,它屬于一個獨立的函數或是其它類的成員函數。所以出現了兩次它的定義,肯定會報redefinition的錯。
解決方法當然也很簡單,一個函數,可以不能定義兩次,但是可以被申明無數次:類內聲類,類外實現:
#include<iostream> using namespace std; template<class T> class A { public: friend void test(int x); private: T a; //將template<class T>中的T替換成long long. }; void test(int x) { cout << 1; } int main() { A<int> x; A<long long> y; test(1); cout << endl << "沒問題" << endl; return 0; }
運行結果:
浙公網安備 33010602011771號