我不明白,为什么我们定义标题通常(非模板)类的静态变量,我们有链接错误,但在模板情况下,所有工作正常,并且我们将所有的翻译单位之间的静态变量的单个实例:
这是模板头(template.h):
// template.h
template<typename T>
class Templ {
public:
static int templStatic;
};
template<typename T> Templ<T>::templStatic = 0;
这是使用模板性的第一单元(unit1.cpp)
// unit1.cpp
#include "template.h"
int method1() {
return Templ<void>::templStatic++;
}
这里第二单元(unit2.cpp):
// unit2.cpp
#include "template.h"
int method2() {
return Templ<void>::templStatic++;
}
最后,main.cpp中:
// main.cpp
#include <iostream>
int method1();
int method2();
int main(int argc, char** argv) {
std::cout << method1() << std::endl;
std::cout << method2() << std::endl;
}
编译,链接和执行此代码后,我们将有以下的输出:
0
1
那么,为什么在模板情况下,所有工作正常(和预期)?编译器或链接如何处理这个(我们可以编译编译器的分离调用每个.cpp文件,然后用caling来连接,所以编译器和连接不“看”在同一时间的所有.cpp文件链接它们)?
PS:我的编译器:msvcpp 9(但MinGW的检查太)
这是因为静态数据成员的定义本身就是一个模板。允许这是必要的,你被允许有一个函数模板不是在程序内联多次同样的道理。你需要的模板来生成所产生的实体(比如,一个函数,或静态数据成员)。如果你将不会被允许把静态数据成员的定义,你会如何实例以下
template<typename T>
struct F {
static int const value;
};
template<typename T>
int const F<T>::value = sizeof(T);
它不知道什么T
是 - 标准说类模板外的定义是一个模板定义,其中参数是从它的类模板的所有者继承。
我做了一些实验与海湾合作委员会。在下文中,我们有F<float>::value
的一个隐式实例,并F<char>::value
的一个明确的专业化其中有一个.cpp文件中定义时多次包括不会造成重复的符号错误。
// Translation Unit 1
template<typename T>
struct F {
static int value;
};
template<typename T>
int F<T>::value = sizeof(T);
// this would belong into a .cpp file
template<> int F<char>::value = 2;
// this implicitly instantiates F<float>::value
int test = F<float>::value;
int main() { }
第二个翻译单元包含相同的静态数据成员的只是一个隐式实例
template<typename T>
struct F {
static int value;
};
template<typename T>
int F<T>::value = sizeof(T);
int test1 = F<float>::value;
下面是我们得到与海湾合作委员会的东西 - 它使每一个隐式实例为弱符号,它坚持到它自己的部分在这里。当有链接时存在它们的多个弱符号不会导致错误。相反,链接器会选择一个实例,并丢弃其他的假设所有的人都是一样的
objdump -Ct main1.o # =>
# cut down to the important ones
00000000 l df *ABS* 00000000 main1.cpp
0000000a l F .text 0000001e __static_initialization_and_destruction_0(int, int)
00000000 l d .data._ZN1FIfE5valueE 00000000 .data._ZN1FIfE5valueE
00000028 l F .text 0000001c global constructors keyed to _ZN1FIcE5valueE
00000000 g O .data 00000004 F<char>::value
00000000 g O .bss 00000004 test
00000000 g F .text 0000000a main
00000000 w O .data._ZN1FIfE5valueE 00000004 F<float>::value
所以,我们可以看到F<float>::value
是弱符号,这意味着连接可以在链接时看到的这些倍数。 test
,main
和F<char>::value
是全球性的(非弱)符号。链接main1.o
和main2.o
在一起,我们可以看到在地图输出(-Wl,-M
)以下
# (mangled name)
.data._ZN1FIfE5valueE
0x080497ac 0x4 main1.o
0x080497ac F<float>::value
这表明,实际上它丢弃所有除一个实例。
有解决方案,你可以创建一个父类,把静态变量中,然后让你的模板类继承私下,这里是一个例子:
class Parent
{
protected:
static long count;
};
long Parent::count = 0;
template<typename T>
class TemplateClass: private Parent
{
private:
int mKey;
public:
TemplateClass():mKey(count++){}
long getKey(){return mKey;}
}
int main()
{
TemplateClass<int> obj1;
TemplateClass<double> obj2;
std::cout<<"Object 1 key is: "<<obj1.getKey()<<std::endl;
std::cout<<"Object 2 key is: "<<obj2.getKey()<<std::endl;
return 0;
}
输出将是:
Object 1 key is: 0
Object 2 key is: 1
这是因为模板代码是不是源代码;它是关于如何编写源代码指令。
非模板静态变量是实际的源代码,编译器将尝试通过在两次东西做的正是你说什么。因此,你必须初始化在.cpp文件的静态变量,只引用它在描述类.h文件。这相当于通过的extern声明全局变量。
当编译器看到
template<class T> Templ{...};
它什么都不做,除了使该模板存在一个音符。至于它而言,没有与相关TEMPL没有源代码。当您第一次实际上是指
Templ<int> Instance
编译器着眼于与TEMPL相关联的所有的模板<>码并使用它来构造一个h和一个cpp文件(其只存在于编译的持续时间)。这些文件可能是这样的:
Temple_int.h
class Templ_int{
public:
static int templStatic;
};
Templ_int.cpp
#include "Templ_int.h"
Templ_int::templStatic = 0;
而每
Templ<int>
成为Templ_int。因此,源代码以初始化静态变量只存在一次,在由编译器创建的cpp文件。 (显然,这个过程中的实际具体编译器的实现将是对创建具有类似名称的类模板稳健等)