user20562802
user20562802

Reputation:

Why class templates don't allow const static data member to be defined outside the class as constexpr

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

Answers (1)

Alan
Alan

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

Related Questions