簡體   English   中英

c ++模板實例化

[英]c++ template instantiation

我有一個像下面這樣的模板類。

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文件,它將如何工作。

將為S的每個不同值實例化模板。

如果將方法實現移至其他文件,則需要#include該文件。 (例如,Boost將.ipp約定用於需要#include的此類源文件)。

如果要最小化模板實例化生成的代碼量(因此需要在.ipp文件中使用),則應嘗試通過消除對S的依賴關系來將其排除在外。例如,您可以派生從(私有)基類中獲取,該基類以S作為參數提供成員函數。

實際上,這完全取決於編譯器。 只需要為其輸入生成正確的代碼即可。 為了做到這一點,必須遵循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());
}

匯編器輸出如下:

?test@@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    ?useA@@YAXAAV?$A@$04@@H@Z       ; useA

如您所見,內聯了ctor和對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    ?useA@@YAXAAV?$A@$0BJ@@@H@Z     ; useA

看到編譯器優化for循環的巧妙方法非常有趣。 對於所有使用memset()的過早的優化器,我會說傻瓜。

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

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

如果我使用不同的S值實例化上述類,則編譯器會創建A()和MaxLenth()函數的不同實例嗎? 還是會創建一個實例並將S作為某種參數傳遞?

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

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

您的意思是如果將A的定義放入頭文件中,但在cpp文件中定義成員函數MaxLength 好吧,如果您的類模板的用戶想要調用MaxLength ,則編譯器希望查看其代碼,因為它希望使用S的實際值實例化其副本。 如果沒有可用的代碼,則假定以其他方式提供了該代碼,並且不生成任何代碼:

丙型肝炎

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

丙型肝炎

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

如果現在僅使用類模板A為代碼包括include A.hpp ,則編譯器將看不到MaxLength的代碼,並且不會生成任何實例化。 您有兩種選擇:

  • 還要包含文件A.cpp ,以便編譯器可以看到它的代碼,或者
  • 為您知道將要使用的S值提供顯式實例化。 對於這些值,您無需提供MaxLength的代碼

對於第二個選項,這是通過在A.cpp放置如下一行來完成的:

template class A<25>;

由於您顯式實例化了S=25模板副本,因此編譯器現在可以生存,而無需查看A<25>成員函數的代碼。 如果您不執行上述兩個選項中的任何一個,則鏈接器將拒絕創建最終的可執行文件,因為仍然缺少所需的代碼。

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

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

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

它將創建A()MaxLength()兩個不同版本,這些版本將返回編譯時常量。 簡單return S; 將有效地進行編譯,甚至在可能的情況下內聯。

如果為不同的類型或參數實例化該類,則編譯器將創建該類的不同實例。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM