编译器与《 XIV:双重定义的厄运》作斗争,共同出演《可疑宣言》!
编译器,全部使用-O0
或Debug模式:
摘要:
template <> const std::string TemplatedClass::x; // .h file
template <> const std::string TemplatedClass::x= "string"; // .cpp file
在定义模板的专用静态成员变量时,我遇到了一个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++会这样做,但是如果存在声明,则会产生双重初始化错误。
最后
病态?
定义明确的程序?
最佳答案
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/