2008-12-08 168 views
0

我有一個類似下面的模板類。C++模板實例化

template<int S> class A 
{ 
private: 
    char string[S]; 

public: 
    A() 
    { 
    for(int i =0; i<S; i++) 
    { 
     . 
     . 
    } 
    } 

    int MaxLength() 
    { 
    return S; 
    } 
}; 

如果我用S的不同值實例化上面的類,編譯器是否會創建A()和MaxLenth()函數的不同實例?或者它會創建一個實例並將S作爲某種參數傳遞?

如果將A和Maxlength的定義移動到不同的cpp文件,它將如何工作。

回答

5

模板被實例化S.

的每個不同的值,你需要#包括該文件。 (例如Boost使用.ipp慣例來處理需要#included的源文件)。

如果您想最小化使用模板實例化生成的代碼量(因此需要在.ipp文件中使用該代碼),您應該嘗試通過消除對S的依賴性來將其分解。例如,你可以從一個(私有)基類派生,它提供了S作爲參數的成員函數。

1

無論使用哪個S,該函數的單獨版本都會被編譯到您實例化的每個不同S的代碼中。

0

它將創建兩個不同版本的A()MaxLength(),它們將返回編譯時常量。簡單的return S;將盡可能高效地編譯,甚至可以內聯。

0

如果您將實例化爲不同類型或參數,編譯器將創建類的不同實例。如果將方法實現到不同的文件

5

其實這完全取決於編譯器。只需要爲其輸入生成正確的代碼。爲了這樣做,它必須遵循C++標準,因爲這解釋了什麼是正確的。在這種情況下,它表示編譯器必須在進程的一個步驟中將具有不同參數的模板實例化爲不同類型,這些類型稍後可以由相同的代碼表示,或者不是,它完全取決於編譯器。

編譯器很可能會內聯至少MaxLength(),但也可能是你的ctor。否則,它可能很好地生成你的ctor的一個實例,並通過/讓它從別處檢索S.要知道的唯一方法是檢查編譯器的輸出。

因此,爲了確切知道我決定列出VS2005在發佈版本中所做的工作。我編寫的文件是這樣的:

template <int S> 
class A 
{ 
    char s_[S]; 
public: 
    A() 
    { 
    for(int i = 0; i < S; ++i) 
    { 
     s_[i] = 'A'; 
    } 
    } 
    int MaxLength() const 
    { 
    return S; 
    } 
}; 

extern void useA(A<5> &a, int n); // to fool the optimizer 
extern void useA(A<25> &a, int n); 

void test() 
{ 
    A<5> a5; 
    useA(a5, a5.MaxLength()); 
    A<25> a25; 
    useA(a25, a25.MaxLength()); 
} 

彙編器輸出如下:

[email protected]@YAXXZ PROC     ; test, COMDAT 

[snip] 

; 25 : A<5> a5; 

mov eax, 1094795585    ; 41414141H 
mov DWORD PTR _a5$[esp+40], eax 
mov BYTE PTR _a5$[esp+44], al 

; 26 : useA(a5, a5.MaxLength()); 

lea eax, DWORD PTR _a5$[esp+40] 
push 5 
push eax 
call [email protected]@[email protected][email protected]@[email protected]  ; useA 

正如你可以看到兩個構造函數和調用的MaxLength()內聯。正如你現在可以猜測它同樣與A < 25>類型:

; 28 : A<25> a25; 

mov eax, 1094795585    ; 41414141H 

; 29 : useA(a25, a25.MaxLength()); 

lea ecx, DWORD PTR _a25$[esp+48] 
push 25     ; 00000019H 
push ecx 
mov DWORD PTR _a25$[esp+56], eax 
mov DWORD PTR _a25$[esp+60], eax 
mov DWORD PTR _a25$[esp+64], eax 
mov DWORD PTR _a25$[esp+68], eax 
mov DWORD PTR _a25$[esp+72], eax 
mov DWORD PTR _a25$[esp+76], eax 
mov BYTE PTR _a25$[esp+80], al 
call [email protected]@[email protected][email protected]@@[email protected]  ; useA 

這是非常有趣的,看看聰明的方式編譯器優化的循環。對於那些使用memset()的所有那些過早的優化器,我會說你傻瓜。

如果將A和Maxlength的定義移動到不同的cpp文件,它將如何工作。

它可能不會編譯(除非你只在該cpp文件中使用A)。

+0

不完全。編譯器需要爲每個S值生成一個單獨的類型。如果編譯器認爲它是安全的,它當然可以通過將多個實例合併回來或內聯來優化。但是,標準要求它們被視爲獨立的類型。 – jalf 2008-12-08 16:28:14

1

A<S>::MaxLength()是如此微不足道,它將完全內聯。因此,將會有0份。 A<S>::A()看起來更復雜,因此它可能會導致生成多個副本。只要代碼按照預期工作,編譯器當然可以決定不這樣做。

您可能想看看您是否可以將循環移動到A_base :: A_base(int S)。

2

如果我實例與S的 不同的值以上的類,將所述 編譯器創建 A()和MaxLenth()函數的不同實例?或者將 它創建一個實例並通過S 作爲某種參數?

編譯器將爲每個不同的參數值實例化類模板的不同副本。關於成員函數,它將爲每個不同的值S實例化每個的不同副本。但是,與非模板類的成員函數不同,它們只會在實際使用時纔會生成。

如果將A和Maxlength的定義移動到不同的cpp文件,它將如何工作。

你是說如果你把A的定義放到頭文件中,但是在cpp文件中定義成員函數MaxLength?那麼,如果你的類模板的用戶想調用MaxLength,那麼編譯器就想看到它的代碼,因爲它想用實際值S實例化它的副本。如果沒有可用的代碼,它假設代碼另有規定外,不產生任何代碼:

A.hpp

template<int S> class A { 
public: 
    A() { /* .... */ } 
    int MaxLength(); /* not defined here in the header file */ 
}; 

A.cpp

template<int S> int 
A<S>::MaxLength() { /* ... */ } 

如果您現在僅針對使用類模板A的代碼包含A.hpp,那麼編譯器將不會看到MaxLength的代碼並不會產生任何實例。你有兩個選擇:

  • 包括文件A.cpp太多,所以編譯器看到它的代碼,或
  • 提供的S值的顯式實例你知道你將使用。對於那些價值觀,你將不再需要提供的MaxLength

代碼對於第二個選項,這是通過將一個類似線下內A.完成CPP

template class A<25>; 

編譯器將能夠在不現在看到對於A<25>成員函數代碼中生存,因爲你明確實例化模板的副本S=25。如果您沒有執行上述兩個選項中的任何一個,鏈接器將拒絕創建最終的可執行文件,因爲仍然存在缺少所需的代碼。