代码之家  ›  专栏  ›  技术社区  ›  cybevnm

模板静态变量

  •  55
  • cybevnm  · 技术社区  · 17 年前

    我不明白,为什么我们在头文件中定义了普通(非模板)类的静态变量,我们会有链接器错误,但在模板的情况下,所有工作正常,而且我们会在所有翻译单元中有一个静态变量实例:

    // 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++;
    }
    

    此处第二单元(单元2.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
    

    PS:My编译器:msvcpp 9(但也在mingw上进行了检查)

    3 回复  |  直到 11 年前
        1
  •  66
  •   Johannes Schaub - litb    17 年前

    template<typename T>
    struct F {
      static int const value;
    };
    
    template<typename T>
    int const F<T>::value = sizeof(T);
    

    不知道是什么 T


    我用GCC做了一些实验。在下面,我们有一个 F<float>::value ,以及 F<char>::value

    // 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;
    

    这是我们使用GCC得到的结果——它将每个隐式实例化变成一个弱符号,并将其粘贴到它自己的部分中。当链接时存在多个弱符号时,弱符号不会导致错误。相反,链接器将选择一个实例,并丢弃其他实例(假设所有实例都相同)

    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<浮动>::价值 是弱符号,表示链接器在链接时可以看到多个弱符号。 test , main 和 F<char>::价值 是全局(非弱)符号。连接 main1.o 和 main2.o 我们一起在地图输出中看到( -Wl,-M

    # (mangled name)
    .data._ZN1FIfE5valueE
        0x080497ac        0x4 main1.o                                             
        0x080497ac                F<float>::value
    

    这表明它实际上删除了除一个实例之外的所有实例。

        2
  •  3
  •   POSTHUMAN    9 年前

    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
    
        3
  •  1
  •   user1167758    7 年前

    因为模板代码不是源代码;这是关于如何编写源代码的说明。

    非模板静态变量是实际的源代码,编译器将通过两次包含某些内容来尝试完全按照您所说的做。因此,您必须在.cpp文件中初始化静态变量,并且只在描述类的.h文件中引用它。它相当于通过extern声明的全局变量。

    当编译器看到

    template<class T> 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>
    

    成为一个模板。 因此,初始化静态变量的源代码在编译器创建的.cpp文件中只存在一次。

    推荐文章