The compiler fights with XIV: Doom of Duplicitous Double Definition, starring The Dubious Declaration!
Compilers, all with -O0 or debug mode:
- g ++ 5.2.0
- clang ++ 3.6.0
- VC ++ 18.00.40629 (MSVC 2013, update 5)
Summary:
- Is VC ++ erroneous in refusing to declare and define a specialized static member variable of a template template with syntax?
template <> const std::string TemplatedClass::x;
- Does deleting an advertisement in the header file lead to the fact that otherwise a certain program will be poorly formed?
- If so, is there a friendly VC ++ way to declare the specialization of a static member variable of a template class?
When creating the MCVE problem that I encountered defining specialized static template member variables, I found an interesting variation in behavior between VC ++, GCC, and Clang with respect to the declaration of the specialized specialized static member variables. In particular, the syntax
template <> const std::string TemplatedClass<int>::x;
seems mortally insulted by VC ++, which responds with complaints about several definitions:
error C2374: 'member' : redefinition; multiple initialization
while gcc and clang take it in step.
Research
I assume the latter two are correct because they usually exist, and also because the above syntax is from the answer regarding the static initialization of a member of the specialized template class , which quotes paragraph 14.7.3 / 15 from the 2010 standard that template<> X Q<int>::x is a declaration, not a definition. I took the liberty of tracking the equivalent paragraph of Project N4296, thinking that it could change in the past. It has, but only in the fact that it has moved two paragraphs up and contains additional clarification:
14.7.3 / 13
An explicit specialization of a static data element of a template or an explicit specialization of a template of a static data element is a determination if the declaration includes an initializer; otherwise it is an ad. [Note. Defining a template static data element that requires default initialization should use a completion-bound list:
template<> X Q<int>::x; // declaration template<> X Q<int>::x (); // error: declares a function template<> X Q<int>::x { }; // definition
- end note]
This seems perfectly understandable to me, but VC ++ seems to have a different interpretation. I tried just to comment on the violation statement, and no compilers complain that it would seem to solve my problems, but not because in paragraph 6 it says: (worrying about my attention)
14.7.3 / 6
If a template, a member template, or a member of a class template is explicitly specialized, then this specialization must be declared before the first use of this specialization, which will cause an implicit instantiation, in each translation unit in which such use is used; no diagnostics required. If the program does not provide a definition for explicit specialization, and either specialization is used in such a way as to implicitly create an instance, or the member is a function of a virtual member, the program is poorly formed, and diagnostics are not required. An implicit instance is never created for explicit specialization, which is declared but not defined.
It gives examples, but all of them are intended for specialized functions after their use or specialization of enumerations of members and classes of the template type, which, I am sure, are not related to this problem. However, the source words p13 seem to imply that declaring a specialized static member variable is also an explicit specialization, at least when using the illustrated syntax.
Mcve
The testing that I used for my experiments can be found in Coliru, apologizing for StackedCrooked for the rather active command line. The following is a shortened version:
main.cpp
#include <iostream> // 'header' file #include "test.h" int main(){ std::cout << test::FruitNames<Fruit::APPLE>::lowercaseName(); }
test.h (ad is not commented out)
test.h (commented on ad)
#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"; }
Output
Both gcc and clang output
apple
with or without specialization declaration in test.h. VC ++ will do this if the declaration in test.h is commented out, but will lead to a double initialization error, if present.
Finally
- Is it incorrect for VC ++ to reject the declaration / explicit specialization syntax for a static member variable of a template class, as indicated earlier, or is this a valid but not required diagnostic error?
- Does deleting an ad cause the program to form poorly?
- If it is poorly formed without an ad, how do I get VC ++ to play well with a well-defined program?