I wrote the following snippet that compiles with clang but gcc and msvc rejects it. Demo
#include <iostream>
#include <iomanip>
//wrapper
template<typename T>
struct Wrapper
{
inline static T value{};
};
template<> int Wrapper<int>::value = 42; //clang:OK, gcc:Nope, MSVC:Nope
template<> double Wrapper<double>::value = 3.14; //clang:OK, gcc:Nope, MSVC:Nope
int main()
{
}
GCC says:
<source>:11:30: error: duplicate initialization of 'Wrapper<int>::value'
11 | template<> int Wrapper<int>::value = 42;
| ^~~~~
<source>:12:36: error: duplicate initialization of 'Wrapper<double>::value'
12 | template<> double Wrapper<double>::value = 3.14;
|
So which compiler is right here?
What is the workaround?
7
The program is well-formed as per temp.expl.spec#15. First note that inline static T value{};
is a definition. Now, from [temp.expl.spec]:
A member or a member template of a class template may be explicitly specialized for a given implicit instantiation of the class template, even if the member or member template is defined in the class template definition.
An explicit specialization of a member or member template is specified using the syntax for explicit specialization.
(emphasis mine)
This means that even though there is a definition for the member value
inside the class template, we’re allowed to provide an explicit specialization for that member.
Thus gcc and msvc are wrong in giving a diagnostic.
What is the workaround?
There are multiple workaround for this.
Workaround 1
One way is to use the old way of out of class definition as shown below:
template<typename T>
struct Wrapper
{
static T value; //not used inline here for workaround
};
template<typename T> T Wrapper<T>::value{}; //out of class definition
template<> int Wrapper<int>::value = 42; //gcc starts accepting now
int main()
{
}
Workaround 2
Another way is to use constexpr if
and lambda as shown below:
template <typename T>
struct Wrapper {
static inline T value = [] {
if constexpr (std::is_same_v<T, double>) return 3.14;
else if constexpr (std::is_same_v<T, int>) return 42;
else return T{};
}();
};
Demo
2