我有一个类似下面的模板类。
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文件,它将如何表现。
答案 0 :(得分:5)
将为S的每个不同值实例化模板。
如果将方法实现移动到其他文件,则需要#include该文件。 (例如,Boost对需要#included的源文件使用.ipp
约定。
如果要最小化使用模板实例化生成的代码量(因此需要在.ipp
文件中提供),您应该尝试通过删除对S的依赖性来解决它。因此,例如,您可以从(私有)基类派生,该基类提供以S作为参数的成员函数。
答案 1 :(得分: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());
}
汇编程序输出如下:
?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&lt; 25&gt;也是如此。类型:
; 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)。
答案 2 :(得分: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包含include A.hpp 代码,那么编译器将看不到MaxLength
的代码,也不会生成任何实例化。您有两种选择:
S
值提供显式实例化。对于这些值,您无需提供MaxLength
对于第二个选项,可以通过在 A.cpp 中放置如下所示的行:
template class A<25>;
编译器将能够在不查看A<25>
成员函数的代码的情况下继续存在,因为您明确地为S=25
实例化了模板的副本。如果您不执行上述两个选项中的任何一个,链接器将拒绝创建最终的可执行文件,因为仍然缺少所需的代码。
答案 3 :(得分:1)
无论在何处使用S,该函数的单独版本都将编译到您实例化的每个不同S的代码中。
答案 4 :(得分:1)
A<S>::MaxLength()
是如此微不足道,它将完全内联。因此,将有0份。 A<S>::A()
看起来更复杂,因此可能会导致生成多个副本。只要代码按预期工作,编译器当然可以决定不这样做。
您可能想看看是否可以将循环移动到A_base :: A_base(int S)。
答案 5 :(得分:0)
它将创建两个不同版本的A()
和MaxLength()
,它们将返回编译时常量。简单的return S;
将被有效编译,甚至可能在内联。
答案 6 :(得分:0)
如果您为不同的类型或参数实例化它,编译器将创建该类的不同实例。