解析模板类的专用静态成员变量的定义 [英] Resolving Definitions of Specialized Static Member Variables of Templated Classes

查看:125
本文介绍了解析模板类的专用静态成员变量的定义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编译器与XIV:双重疑义的厄运抗战,由《可疑宣言》共同主演!

全部使用-O0或调试模式的编译器:

Compilers, all with either -O0 or Debug mode:

  • 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的行为方面遇到了一个有趣的变化,即声明所述专用静态成员变量.具体来说,语法

    While making an MCVE of a problem I was having with defining specialized static member variables of a template, I encountered an interesting variation in behavior between VC++, GCC and Clang with respect to the declaration said specialized static member variables. Specifically, the syntax

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

    似乎致命地冒犯了VC ++,而VC ++则对多种定义提出了抱怨:

    seems to mortally offend VC++, which responds with complaints of multiple definitions:

    error C2374: 'member' : redefinition; multiple initialization
    

    同时gcc和clang大步向前.

    while both gcc and clang take this in stride.

    我认为后两者是正确的,因为它们通常是正确的,也因为上述语法来自有关静态成员的答案专用模板类的初始化,引用了2010年标准的14.7.3/15段,指出template<> X Q<int>::x是声明,而不是定义.我自由地追查了N4296草案的同等段落,认为在这段时间内它可能会发生变化.它具有但仅在于将第二段向上移动并包含其他说明:

    I'm assuming the latter two are correct because they usually are, and also because the above syntax is from an answer regarding static member initialization of a specialized template class, which quotes paragraph 14.7.3/15 from the standard of 2010 in stating that template<> X Q<int>::x is a declaration, not a definition. I took the liberty of tracking down the equivalent paragraph of draft N4296, thinking it could have changed in the intervening time. It has, but only in that it's moved two paragraphs up and contains additional clarification:

    14.7.3/13

    如果声明包含初始化程序,则模板的静态数据成员的显式专业化或静态数据成员模板的显式专业化是定义.否则,它是一个声明. [注意:需要默认初始化的模板的静态数据成员的定义必须使用braced-init-list:

    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
    

    —尾注]

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

    This seems pretty clear to me, but VC++ seems to have a different interpretation. I've tried simply commenting out the offending declaration, and no compilers complain, which would seem to solve my troubles, but doesn't because paragraph 6 has this to say: (worrying emphasis mine)

    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的最初单词似乎暗示着,至少在使用所示语法的情况下,专用静态成员变量的声明也是显式的专用.

    It provides examples, but all of them are for specializing functions after they're used or specializing member enums and classes of a templated type, which I'm fairly certain don't apply to this problem. However, the initial words of p13 seem to imply that the declaration of the specialized static member variable is also an explicit specialization, at least when using the illustrated syntax.

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

    The test I used for my experimentation can be found on Coliru, with apologies to StackedCrooked for the fairly involved command line. A much shortened version is below:

    main.cpp

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

    test.h (声明未注释掉)
    test.h (声明已注释掉)

    test.h (declaration not commented out)
    test.h (declaration commented out)

    #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都将输出

    Output

    Both gcc and clang will output

    apple
    

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

    with or without the specialization declaration in test.h. VC++ will do so if the declaration in test.h is commented out, but will produce a double initialization error if it is present.

    • 如前所述,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?
    • Does the removal of the declaration cause the program to be ill-formed?
    • If it is ill formed without the declaration, how do I get VC++ to play nice with a well-defined program?

    推荐答案

    如前所述,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?

    是的,这是 VC ++中的错误.显然,它已在 Visual Studio 2019版本16.5预览版2 中修复.

    Yes, this is a bug in VC++. It has apparently been fixed in Visual Studio 2019 version 16.5 Preview 2.

    删除声明会导致程序格式错误吗?

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

    您从标准中引用的内容似乎暗示了这一点. 其他人同意.

    Your quote from the standard seems to suggest that. Other people agree.

    如果在没有声明的情况下格式不正确,如何使VC ++与定义良好的程序配合使用?

    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

    As a workaround, you can specialize the whole class and then define the member without the template<> syntax. See Amir Kirsh's answer to a similar question: https://stackoverflow.com/a/58583521/758345

    或者,您可以在标头中定义和初始化变量,并将其标记为内联(自c ++ 17 起):

    Alternatively, you could define and initialize your variable in your header and mark it as inline (since c++17):

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

    这篇关于解析模板类的专用静态成员变量的定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆