Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/visual-studio-2012/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 将静态变量与模板一起使用_C++_Templates_Static_Visual C++ 6 - Fatal编程技术网

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
)然后新实例将有一个新的static
count
原始问题的静态变量在全局范围内,即不在类中。您的示例在模板类中有静态变量。这两种情况完全不同。我猜,这样做的基本原理是不使链接器的工作太困难:链接器可以同时拥有这两种情况指相同的
计数
,或两者都指不同的
计数
s。