The problem boils down to:
is a constexpr constructor that default-initializes
some non-static data member of builtin type valid,
if it is not used?
tl;dr:
For a non-template constructor,
no, it is invalid to leave any non-static data members uninitialized.
For a template constructor, yes,
it is valid to have some (but not all, no diagnostic required)
instantiated template specializations
for which the instantiated constructor does not meet the requirements
of a constexpr constructor.
In this case, GCC is right, whereas Clang is wrong.
GCC gives the following error message which is very informative:
prog.cc:8:13: error: explicitly defaulted function 'constexpr B::B()' cannot be declared as 'constexpr' because the implicit declaration is not 'constexpr':
8 | constexpr B() = default;
| ^
prog.cc:3:13: note: defaulted constructor calls non-'constexpr' 'A<T>::A() [with T = int]'
3 | constexpr A() = default;
| ^
prog.cc:3:13: note: 'A<T>::A() [with T = int]' is not usable as a 'constexpr' function because:
prog.cc:4:5: note: defaulted default constructor does not initialize 'int A<int>::v'
4 | T v;
| ^
live demo
Note that the error is raised on the constructor of B
,
instead of that of A
,
whose constructor is merely "not usable as a constexpr
function
because [the] defaulted default constructor
does not initialize int A<int>::v
."
Per [dcl.constexpr]/4:
The definition of a constexpr constructor shall satisfy the following
requirements:
- the class shall not have any virtual base classes;
- each of the parameter types shall be a literal type.
In addition, either its function-body shall be = delete
, or it
shall satisfy the following requirements:
- [...]
- every non-variant non-static data member and base class subobject
shall be initialized ([class.base.init]);
- [...]
Here, v
is of type int
, and is not initialized.
Therefore, it seems that the constructor of A
cannot be declared constexpr
.
However, [dcl.constructor]/6 says:
If the instantiated template specialization of a constexpr function
template or member function of a class template would fail to satisfy
the requirements for a constexpr function or constexpr constructor,
that specialization is still a constexpr function or constexpr
constructor, even though a call to such a function cannot appear in a
constant expression. If no specialization of the template would
satisfy the requirements for a constexpr function or constexpr
constructor when considered as a non-template function or constructor,
the template is ill-formed, no diagnostic required.
Therefore, the constructor of A
that is declared constexpr
is actually valid,
even when instantiated for T = int
!
The problem is the constructor of B
.
B
is an ordinary class (as opposed to a class template),
and for its constructor to be (merely) declared constexpr
,
A<int>
must have a constexpr
constructor,
which is not the case.
Therefore, this code should be rejected, as GCC does.
(Note that both compilers reject initialization of such a type,
for example:
A a{};
B b{};
The above code is rejected by both compilers.)
As mentioned in a comment,
initialize A::v
and GCC (and the standard) will be happy.
A::v
and gcc will be happy. – Swordfish