题
我有一个模板类中定义的一标题的文件这样的。在这里,我必须限定一个静态的变量,以及:
#ifndef TEST1_H_
#define TEST1_H_
void f1();
static int count;
template <class T>
class MyClass
{
public:
void f()
{
++count;
}
};
#endif
我有定义的主要()function在一个不同的cpp文件是这样的:
int main(int argc, char* argv[])
{
MyClass<int> a;
a.f();
f1();
cout<<"Main:" << count << "\n";
return 0;
}
我已经实现的功能的f1()在不同的cpp文件是这样的:
void f1()
{
MyClass<int> a;
a.f();
cout<<"F1: " <<count <<"\n";
}
当我编写这个用VC6,我得到了输出"F1:0主要:2的"。这怎么可能?另外,在一般情况下如何我应该把手的如果我想要使用静态变量与模板?
解决方案
您获得了同一个变量的两个副本,因为您已在头文件中声明了一个静态变量。当您以这种方式声明全局变量static
时,您说它是编译单元(.o
文件)的本地变量。由于您在两个编译单元中包含标头,因此您将获得两个count
的副本。
我认为你真正想要的是一个与模板类的每个实例相关联的静态模板成员变量。它看起来像这样:
template <class T>
class MyClass
{
// static member declaration
static int count;
...
};
// static member definition
template<class T> int MyClass<T>::count = 0;
这将为您计算模板的每个实例化。也就是说,您将计算MyClass<int>
,MyClass<foo>
,MyClass<bar>
等等。f1()
现在看起来像这样:
void f1() {
MyClass<int> a;
a.f();
cout<<"F1: " << MyClass<int>::count <<"\n";
}
如果您想要计算MyClass的所有实例(无论其模板参数如何),您都需要使用全局变量。
但是,您可能不希望直接使用全局变量,因为在初始化之前存在使用它的风险。你可以通过创建一个返回对你的计数的引用的全局静态方法来解决这个问题:
int& my_count() {
static int count = 0;
return count;
}
然后从你的班级中访问它:
void f() {
++my_count();
}
这将确保计数在使用之前被初始化,无论您从哪个编译单元访问它。有关详细信息,请参阅有关静态初始化顺序的C ++常见问题解答
其他提示
将静态声明放在头文件中将导致每个.cpp文件获得自己的变量版本。所以两个cout语句正在打印不同的变量。
你期待<!>“F1:1主要:1 <!>”;?您在两个单独的转换单元(即两个目标文件)中实例化MyClass<int>
,并且链接器看到存在重复的模板实例化,因此它放弃了f1
目标文件中的实例化。
您是否通过 /OPT:ICF
或/OPT:REF
到VC6链接器?这可能与删除重复模板实例化有关(或者与普通重复函数相比,重复模板实例化可能是一种特殊情况)。 GCC似乎在某些平台上类似的东西。
无论如何,我不会依赖这种在编译器之间保持一致的行为。此外,更改链接器命令行上的目标文件的顺序可能会影响丢弃哪个实例化。
还有另一个解决方案,你可以创建一个共享父类并将这个静态变量放入其中,然后让你的模板类私下继承它,这是一个例子:
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
我认为这实际上是 未定义的行为.
根据C++14[基本的。def.odr]/6:
可以有多个定义的一[...]的一个类模板[...]中提供的程序,每个定义出现在一个不同的翻译单位,并提供定义满足以下要求。鉴于这样一个实体命名的
D
定义在多个翻译单位,然后
- 每个定义的D应包括同一序列的令牌;和
- 在每个定义的D,相应的名字,看着根据3.4,应当提到一个实体定义的定义范围内的D,或者应参照同一实体,后载决议(13.3)和后匹配的部分专业化的模板(14.8.3),除了一个名称,可以参照一个非挥发性 const物体内或如果没有联系的对象都具有相同字型的所有定义D,对象是初始化恒的表达(5.19),并对象是不odr使用,并对象具有同样价值的所有定义D;[...]
问题是,在第一 .cpp
文件的名字 count
内 f1
指的是不同的对象比的名字 count
内 f1
在第二 .cpp
文件,从而违反了条件,相应的名称应当提到相同的实体。
他们是不同的对象是因为 static
说明它说,每个翻译单位都有自己的对象的,名称。