Reputation:
I came to know using partial ordering example that we can define a constexpr static data member outside the class as in:
struct A
{
static const double i;
};
constexpr double A::i{};
constexpr double j = A::i; //valid c++ and accepted by all compilers.
But then when I tried to do the same with a class template, I noted that some compiler compiles the program but other reject it. For example, gcc compiles #1
but both clang and msvc rejects #1
. Demo-1.
And if I change double
to int
, then clang and gcc accepts the program(#2
) but msvc still rejects #2
. Demo-2
template<class T>
class C
{
public:
static const double mem;
static const int k;
};
template<class T>
constexpr double C<T>::mem{};
template<class T>
constexpr int C<T>::k{};
int main()
{
constexpr double i = C<double>::mem; //#1: gcc accepts but both clang and msvc rejcects
constexpr int j = C<double>::k; //#2: gcc and clang accepts but msvc rejects
}
I want to know which compiler is right here according to the c++ standard for the two cases 1
and 2
.
For reference, here is the error from clang for #1
:
<source>:14:22: error: constexpr variable 'i' must be initialized by a constant expression
14 | constexpr double i = C<double>::mem; //#1: gcc accepts but both clang and msvc rejcects
| ^ ~~~~~~~~~~~~~~
<source>:14:27: note: read of non-constexpr variable 'mem' is not allowed in a constant expression
14 | constexpr double i = C<double>::mem; //#1: gcc accepts but both clang and msvc rejcects
| ^
<source>:5:30: note: declared here
5 | static const double mem;
| ^
Note that I am not looking for a workaround as I know I can just define the members inline by replacing const int
with constexpr int
.
Upvotes: 4
Views: 141
Reputation: 1
This is CWG 2800 that points out that although the intention is to have the program well-formed, currently there is no rule that would cause instantiation of Vector<T,N>::mem
and Vector<T,N>::k
.
Note the CWG2800 doesn't exactly explain why/how this is intended to be well-formed so below is the reference from the standard that shows this.
From basic.link#11:
For any two declarations of an entity E:
- If one declares E to be a variable or function, the other shall declare E as one of the same type.
Types are compared after all adjustments of types (during which typedefs ([dcl.typedef]) are replaced by their definitions); declarations for an array object can specify array types that differ by the presence or absence of a major array bound ([dcl.array]). No diagnostic is required if neither declaration is reachable from the other.
(emphasis mine)
This means that since the two declarations static const double mem;
and template<class T> constexpr double C<T>::mem{};
declare the same entity mem
, both of these declarations must declare mem
to be of the same type. And since constexpr
adds const
, the out of class definition also declares the same type(i.e const int
) as the inside one.
Same explanation goes for member k
.
Thus, the program is well-formed.
Upvotes: 1