C++ 将静态变量与模板一起使用
我在头文件中定义了一个模板类,如下所示。在这里,我还定义了一个静态变量:C++ 将静态变量与模板一起使用,c++,templates,static,visual-c++-6,C++,Templates,Static,Visual C++ 6,我在头文件中定义了一个模板类,如下所示。在这里,我还定义了一个静态变量: #ifndef TEST1_H_ #define TEST1_H_ void f1(); static int count; template <class T> class MyClass { public: void f() { ++count; } }; #endif \ifndef TEST1\u H_ #定义TEST1_H_ void f1();
#ifndef TEST1_H_
#define TEST1_H_
void f1();
static int count;
template <class T>
class MyClass
{
public:
void f()
{
++count;
}
};
#endif
\ifndef TEST1\u H_
#定义TEST1_H_
void f1();
静态整数计数;
模板
类MyClass
{
公众:
void f()
{
++计数;
}
};
#恩迪夫
我在不同的cpp文件中定义了main()函数,如下所示:
int main(int argc, char* argv[])
{
MyClass<int> a;
a.f();
f1();
cout<<"Main:" << count << "\n";
return 0;
}
void f1()
{
MyClass<int> a;
a.f();
cout<<"F1: " <<count <<"\n";
}
template <class T>
class MyClass
{
// static member declaration
static int count;
...
};
// static member definition
template<class T> int MyClass<T>::count = 0;
void f1() {
MyClass<int> a;
a.f();
cout<<"F1: " << MyClass<int>::count <<"\n";
}
void f() {
++my_count();
}
intmain(intargc,char*argv[])
{
我的a级;
a、 f();
f1();
cout将静态声明放在头文件中会导致每个.cpp文件获得其自身版本的变量。因此,两个cout语句打印不同的变量。由于在头文件中声明了静态变量,因此您将获得同一变量的两个副本。当您声明全局变量时static
通过这种方式,您可以说它是编译单元(.o
文件)的本地文件。因为您将头包含在两个编译单元中,所以可以得到两个count
副本
我认为您真正需要的是一个与template类的每个实例关联的静态template成员变量
int main(int argc, char* argv[])
{
MyClass<int> a;
a.f();
f1();
cout<<"Main:" << count << "\n";
return 0;
}
void f1()
{
MyClass<int> a;
a.f();
cout<<"F1: " <<count <<"\n";
}
template <class T>
class MyClass
{
// static member declaration
static int count;
...
};
// static member definition
template<class T> int MyClass<T>::count = 0;
void f1() {
MyClass<int> a;
a.f();
cout<<"F1: " << MyClass<int>::count <<"\n";
}
void f() {
++my_count();
}
如果希望对MyClass的所有实例化进行计数(无论其模板参数如何),则需要使用一个全局变量
但是,您可能不希望直接使用全局变量,因为在初始化之前使用它会有风险。您可以通过创建一个全局静态方法来避免此问题,该方法返回对您的计数的引用:
int& my_count() {
static int count = 0;
return count;
}
然后从类中访问它,如下所示:
int main(int argc, char* argv[])
{
MyClass<int> a;
a.f();
f1();
cout<<"Main:" << count << "\n";
return 0;
}
void f1()
{
MyClass<int> a;
a.f();
cout<<"F1: " <<count <<"\n";
}
template <class T>
class MyClass
{
// static member declaration
static int count;
...
};
// static member definition
template<class T> int MyClass<T>::count = 0;
void f1() {
MyClass<int> a;
a.f();
cout<<"F1: " << MyClass<int>::count <<"\n";
}
void f() {
++my_count();
}
这将确保在使用计数之前对其进行初始化,而不管您从哪个编译单元访问计数。有关详细信息,请参阅。是否预期“F1:1 Main:1”?您在两个单独的翻译单元(即两个对象文件)中实例化了MyClass
,链接器发现有一个重复的模板实例化,所以它放弃了f1
的对象文件中的实例化
您是否正在传递到VC6链接器?这可能与删除重复模板实例化有关(或者不相关;与普通的重复函数相比,重复模板实例化可能是一种特殊情况)。GCC似乎在某些平台上起作用
无论如何,我不相信这种行为在编译器之间是一致的。此外,更改链接器命令行上对象文件的顺序可能会影响丢弃哪个实例化。我认为这实际上是未定义的行为
根据C++14[basic.def.odr]/6:
在一个程序中,类模板[…]的[…]成员函数可以有多个定义,前提是每个定义出现在不同的翻译单元中,并且这些定义满足以下要求。如果在多个翻译单元中定义了名为D
的实体,则
- D的每个定义应包含相同的令牌序列;以及
- 在D的每个定义中,根据3.4查找的相应名称应指在D定义中定义的实体,或在重载解析(13.3)和部分模板专门化匹配(14.8.3)后指同一实体,但名称可指非易失性实体除外
如果对象在D的所有定义中具有相同的文字类型,并且对象使用常量表达式(5.19)初始化,并且该对象未使用odr,并且该对象在D的所有定义中具有相同的值,则该对象具有内部链接或无链接;[…]
问题是,在第一个.cpp
文件中,f1
中的名称count
引用的对象与第二个.cpp
文件中f1
中的名称count
引用的对象不同,因此违反了相应名称应引用同一实体的条件
它们是不同的对象,因为static
说明符表示每个翻译单元都有自己的同名对象。还有另一种解决方案,您可以创建一个共享父类并将此静态变量放入其中,然后让您的模板类私下继承它,下面是一个示例:
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;
}
仅供参考,VC6中曾经存在一个bug,导致它在模板类中获取多个静态变量的定义。之所以发生这种情况,是因为它生成了多个模板代码实例,引用了变量的不同实例。我想它已经随上一个service pack一起消失了。谢谢..这正是我想做的,我想告诉我们使用模板实例化的每个实例初始化静态变量..全局int以完全安全的方式初始化。不需要使用my_count()(这可能有延迟的线程安全初始化开销)我想在我的模板类中使用static,我将使用公认的答案。我原以为在头文件中使用static是个坏主意,但不明白为什么是2&0而不是1&1。感谢您的解释。恐怕我不同意这一点!请看一看;它似乎对我有效。但是说了这一点,我f模板的实例化方式不同(例如:C
)然后新实例将有一个新的staticcount
原始问题的静态变量在全局范围内,即不在类中。您的示例在模板类中有静态变量。这两种情况完全不同。我猜,这样做的基本原理是不使链接器的工作太困难:链接器可以同时拥有这两种情况指相同的计数
,或两者都指不同的计数
s。