我有一个像这样的头文件中定义的模板类 . 这里我也定义了一个静态变量:
#ifndef TEST1_H_
#define TEST1_H_
void f1();
static int count;
template <class T>
class MyClass
{
public:
void f()
{
++count;
}
};
#endif
我已经在不同的cpp文件中定义了main()函数,如下所示:
int main(int argc, char* argv[])
{
MyClass<int> a;
a.f();
f1();
cout<<"Main:" << count << "\n";
return 0;
}
我在不同的cpp文件中实现了函数f1(),如下所示:
void f1()
{
MyClass<int> a;
a.f();
cout<<"F1: " <<count <<"\n";
}
当我用VC6编译它时,输出为“F1:0 Main:2” . 这怎么可能?另外,一般情况下,如果我想将静态变量与模板一起使用,我应该如何处理?
5 回答
您're getting two copies of the same variable because you' ve在头文件中声明了一个静态变量 . 以这种方式声明全局变量
static
时,编译单元的本地're saying it'(.o
文件) . 由于您在两个编译单元中包含标头,因此您将获得两个count
副本 .我认为你真正想要的是一个与模板类的每个实例相关联的静态模板成员变量 . 它看起来像这样:
这将为您计算模板的每个实例化 . 也就是说,你将有
MyClass<int>
,MyClass<foo>
,MyClass<bar>
等的计数.f1()
现在看起来像这样:如果您想要计算MyClass的 all 实例(无论其模板参数如何),您需要使用 global variable .
但是,您可能不希望直接使用全局变量,因为在初始化之前存在使用它的风险 . 你可以通过创建一个返回对你的计数的引用的全局静态方法来解决这个问题:
然后从你的类中访问它,如下所示:
这将确保计数在使用之前被初始化,无论您从哪个编译单元访问它 . 有关详细信息,请参阅C++ FAQ on static initialization order .
将静态声明放在头文件中将导致每个.cpp文件获得自己的变量版本 . 所以这两个cout语句正在打印不同的变量 .
你期待"F1:1 Main:1"?您在两个单独的转换单元(即两个目标文件)中实例化
MyClass<int>
,并且链接器看到存在重复的模板实例化,因此它放弃了f1
的目标文件中的实例化 .您是否将/OPT:ICF or /OPT:REF传递给VC6链接器?这可能与删除重复模板实例化有关(或者与普通重复函数相比,重复模板实例化可能是一种特殊情况) . 海湾合作委员会似乎在某些平台上做了something similar .
无论如何,我不会依赖这种在编译器之间保持一致的行为 . 此外,更改链接器命令行上的目标文件的顺序可能会影响丢弃哪个实例化 .
还有另一个解决方案,你可以创建一个共享父类并将这个静态变量放入其中,然后让你的模板类私下继承它,这是一个例子:
输出将是:
我认为这实际上是未定义的行为 .
根据C 14 [basic.def.odr] / 6:
问题是在第一个
.cpp
文件中,f1
中的名称count
指的是第二个.cpp
文件中f1
内的名称count
不同的对象,因此违反了相应名称应引用同一实体的条件 .它们是不同的对象,因为
static
说明符表示每个翻译单元都使用该名称获得自己的对象 .