[英]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
的代码,并且不会生成任何实例化。 您有两种选择:
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.