C++ c++;模板实例化

C++ c++;模板实例化,c++,templates,C++,Templates,我有一个模板类,如下所示 template<int S> class A { private: char string[S]; public: A() { for(int i =0; i<S; i++) { . . } } int MaxLength() { return S; } }; 模板类别A { 私人: 字符字符串[S]; 公众: () { 对于(int i=0;i无论在何处使用S

我有一个模板类,如下所示

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

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

  int MaxLength()
  {
    return S;
  }
};
模板类别A
{
私人:
字符字符串[S];
公众:
()
{

对于(int i=0;i无论在何处使用S,该函数的一个单独版本将被编译到您实例化的每个不同S的代码中。

它将创建两个不同版本的
a()
MaxLength()
将返回编译时常量。简单的
返回S;
将被高效编译,甚至可能是内联的。

如果您为不同的类型或参数实例化类,编译器将创建不同的类实例。

将为每个不同的S值实例化模板

如果将方法实现移动到另一个文件,则需要包含该文件(例如,Boost对需要包含的源文件使用
.ipp
约定)


如果您想最大限度地减少模板实例化生成的代码量(因此需要在
.ipp
文件中提供),您应该尝试通过删除对S的依赖来消除它。因此,例如,您可以从(private)派生基类,它提供了以S为参数的成员函数。

< P>实际上完全由编译器来完成。它只需要为其输入生成正确的代码。为了做到这一点,它必须遵循C++标准,这说明了什么是正确的。在这种情况下,编译器必须在例示过程中一步到位。使用不同的参数作为不同的类型,这些类型以后可能由相同的代码表示,或者不是,这完全取决于编译器

编译器很可能至少内联MaxLength(),但也可能内联您的ctor。否则,它很可能会生成ctor的单个实例,并传递/让它从其他地方检索。唯一确定的方法是检查编译器的输出

因此,为了确定,我决定列出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());
}
模板
甲级
{
chars_us[s];
公众:
()
{
对于(int i=0;i
汇编程序输出如下所示:

?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
?测试@@YAXXZ程序;测试,COMDAT
[剪报]
;25:a5;
mov eax,1094795585;41H
mov DWORD PTR_a5$[esp+40],eax
mov字节PTR_a5$[esp+44],al
;26:useA(a5,a5.MaxLength());
lea eax,德沃德私人股本公司a5$[esp+40]
推5
推送eax
打电话?useA@@YAXAAV?$A@$04@@H@Z;美国
正如您所看到的,ctor和对MaxLength()的调用都是内联的。现在您可能会猜到,它对类型的作用是相同的:

28:a25; mov eax,1094795585;41H ;29:useA(a25,a25.MaxLength()); lea ecx,德沃德PTR 25$[esp+48] 推送25;000000 19h 推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字节PTR_a25$[esp+80],al 呼叫?useA@@YAXAAV?$A@$0BJ@@@H@Z;美国
看到编译器优化for循环的聪明方法是非常有趣的。对于那些使用memset()的过早优化程序,我想说愚弄你

如果我将A和Maxlength的定义移动到另一个cpp文件,它将如何工作

它可能不会编译(除非您只在该cpp文件中使用A)。

A::MaxLength()
非常简单,它将完全内联。因此,将有0个副本。
A::A()
看起来更复杂,因此可能会导致生成多个副本。当然,编译器可能会决定不这样做,只要代码按预期工作

您可能想看看是否可以将循环移动到A_base::A_base(int S)

如果我用 不同的S值,将 编译器创建不同的 一个()和MaxLenth()函数?或者 它创建一个实例并传递 作为某种论据

编译器将为参数的每个不同值实例化类模板的不同副本。对于成员函数,它将为
S
的每个不同值实例化每个类模板的不同副本。但与非模板类的成员函数不同,它们仅在实际使用时才会生成

如果我将A和Maxlength的定义移动到另一个cpp文件,它将如何工作

你的意思是如果你把
A
的定义放在头文件中,但是在cpp文件中定义成员函数
MaxLength
?如果你的类模板的用户想调用
MaxLength
,编译器想看到它的代码,因为它想用
S
的实际值实例化它的一个副本sn没有可用的代码,它假设代码是以其他方式提供的,并且不会生成任何代码:

A.hpp


编译器现在将能够在看不到
A
的成员函数的代码的情况下生存,因为您显式地实例化了
S=25
的模板副本。如果您不执行上述两个选项中的任何一个,链接器将拒绝创建最终可执行文件,因为仍然缺少所需的代码。

Not quite.编译器需要为S的每个值生成一个单独的类型。如果编译器认为安全,它当然可以通过将多个实例化合并在一起或内联来优化。但是标准要求它们被视为独立类型。很抱歉,我重新阅读了我的文章,并
; 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
template<int S> class A {
public:
    A() { /* .... */ }
    int MaxLength(); /* not defined here in the header file */
};
template<int S> int
A<S>::MaxLength() { /* ... */ }
template class A<25>;