c++ - 解析模板类的专用静态成员变量的定义

标签 c++ templates language-lawyer

编译器与《 XIV:双重定义的厄运》作斗争,共同出演《可疑宣言》!

编译器,全部使用-O0或Debug模式:

  • g++ 5.2.0
  • clang++ 3.6.0
  • VC++ 18.00.40629(MSVC 2013,更新5)

  • 摘要:
  • VC++拒绝使用语法拒绝模板类的专用静态成员变量的声明和定义吗?
  • template <> const std::string TemplatedClass::x; // .h file
    template <> const std::string TemplatedClass::x= "string"; // .cpp file
    
  • 删除头文件中的声明是否会导致本来很好定义的程序格式错误?
  • 如果是这样,有没有一种VC++友好的方法来声明模板化类的静态成员变量的特殊化?


  • 在定义模板的专用静态成员变量时,我遇到了一个MCVE问题,但在声明专用静态成员变量方面,我遇到了VC++,GCC和Clang之间行为的有趣变化。具体来说,语法
    template <> const std::string TemplatedClass<int>::x; // .h file
    template <> const std::string TemplatedClass<int>::x= "string"; // .cpp file        
    

    似乎致命地冒犯了VC++,而VC++则对多种定义提出了提示:
    error C2374: 'member' : redefinition; multiple initialization
    

    而gcc和clang都大步向前。

    研究

    我假设后两者是正确的,因为它们通常是正确的,并且还因为上述语法来自answer regarding static member initialization of a specialized template class,它引用了2010年标准中的14.7.3/15段,指出template<> X Q<int>::x是声明,而不是定义。我自由地追查了N4296草案的同等段落,认为在这段时间内它可能会发生变化。它具有但仅在于将上移了两段并包含其他说明:

    14.7.3/13

    An explicit specialization of a static data member of a template or an explicit specialization of a static data member template is a definition if the declaration includes an initializer; otherwise, it is a declaration. [ Note: The definition of a static data member of a template that requires default initialization must use a braced-init-list:

    template<> X Q<int>::x;      // declaration
    template<> X Q<int>::x ();   // error: declares a function
    template<> X Q<int>::x { };  // definition
    

    — end note ]



    这对我来说似乎很清楚,但是VC++似乎有不同的解释。我只是尝试注释掉有问题的声明,而没有编译器提示,这似乎可以解决我的麻烦,但这并不是因为第6段中的内容是这样的:(我担心的是强调)

    14.7.3/6

    If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the program is ill-formed, no diagnostic required. An implicit instantiation is never generated for an explicit specialization that is declared but not defined.



    它提供了示例,但是所有示例都用于在使用函数后将它们专门化,或将成员枚举和模板化类型的类专门化,我敢肯定地说,这些不适用于此问题。但是,p13的初始单词似乎暗示着,至少在使用所示语法时,专用静态成员变量的声明也是显式的专用。

    MCVE

    我在实验中使用的测试可以在Coliru上找到,对于相当复杂的命令行向StackedCrooked表示歉意。下面是一个大大简化的版本:

    main.cpp
    #include <iostream>
    
    // 'header' file
    #include "test.h"
    
    int main(){
    
      std::cout << test::FruitNames<Fruit::APPLE>::lowercaseName();
    
    }
    

    test.h(声明未注释掉)
    test.h(声明已注释掉)
    #ifndef TEMPLATE_TEST
    #define TEMPLATE_TEST
    
    #include <algorithm>
    #include <locale>
    #include <string>
    
    namespace test{
    
      enum class Fruits{
        APPLE
      };
    
      template <Fruits FruitType_>
      class FruitNames{
        static const std::string name_;
    
      /*...*/
    
      public:
        static std::string lowercaseName() {/*...uses name_...*/}
      };
    
        // should be counted as declaration. VC++ doesn't.
      template <> const std::string FruitNames<Fruits::APPLE>::name_;
    
    } // end namespace test
    
    #endif // TEMPLATE_TEST
    

    test.cpp
    #include "test.h"
    
    namespace test{
    
      template <> const std::string FruitNames<Fruits::APPLE>::name_ = "Apple";
    
    }
    

    输出

    gcc和clang都将输出
    apple
    

    在test.h中带有或不带有特殊化声明。如果将test.h中的声明注释掉,VC++会这样做,但是如果存在声明,则会产生双重初始化错误。

    最后
  • VC++是否不正确,如前所述拒绝模板类的静态成员变量的声明/显式专门化语法,还是允许的但不是强制性的诊断错误?
  • 删除声明是否会使程序成为
    病态?
  • 如果在没有声明的情况下格式不正确,如何使VC++与
    定义明确的程序?
  • 最佳答案

    Is VC++ incorrect to reject the declaration/explicit specialization syntax for the static member variable of a templated class as previously stated, or is it an allowed but not mandatory diagnostic error?



    是的,这是a bug in VC++。它显然已在Visual Studio 2019版本16.5预览2中修复。

    Does the removal of the declaration cause the program to be ill-formed?



    您从标准中引用的内容似乎暗示了这一点。 Other people agree

    If it is ill formed without the declaration, how do I get VC++ to play nice with a well-defined program?



    解决方法是,您可以使整个类专用化,然后在不使用template<>语法的情况下定义成员。请参阅Amir Kirsh对类似问题的回答:
    https://stackoverflow.com/a/58583521/758345

    另外,您可以在 header 中定义和初始化变量,并将其标记为内联(since c++17):

    template <> inline const std::string TemplatedClass::x = "string"; // .h file
    

    关于c++ - 解析模板类的专用静态成员变量的定义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32321006/

    相关文章:

    c++ - 如何将模板函数作为模板类参数传递?

    c++ - 初始化是否需要左值到右值的转换?是 `int x = x;` UB 吗?

    c++ - 在 void 指针中存储整数的往返安全性

    c++ - 如何将列表分成多个 vector ?

    c++ - 如何在 Qt 中使用 QGeopath 清除 Mappolyline

    c++ - 在函数调用之前使用 (void)

    c++ - 可变参数模板示例未编译

    c++ - 具有嵌套命名空间内友元函数的模板类

    c++ - T 是否必须是完整类型才能在 `std::declval<T>` 中使用?

    c++ - 如何将 unsigned int 作为控制台参数?