std::is_constructible_v<A<int&>>
checks whether a variable initialization with ()
initializer is possible. That's never aggregate initialization (not even in C++20), but always value initialization, which will use the default constructor.
Because your class doesn't declare any constructor, it still has an implicit default constructor declared. That one will be called during value initialization.
The implicit default constructor is also not defined as deleted, because none of the points in [class.default.ctor]/2 apply. There is one point for reference members without any default member initializer though.
So this constructor will be chosen in overload resolution and therefore std::is_constructible_v<A<int&>>
is true
. That the instantiation of the default constructor might be ill-formed is irrelevant. Only declarations are checked (the immediate context). So GCC's behavior is not correct.
The only remaining question now is whether the implicit default constructor (or rather the default member initializer?) is supposed to be instantiated from the std::is_constructible
test itself, causing the program to be ill-formed.
As far as I can tell the standard doesn't specify that clearly. The standard says that the noexcept-specifier of a function is implicitly instantiated when it is needed. (see [temp.inst]/15)
It also says that it is needed if it is used in an unevaluated operand in a way that would be an ODR use if it was potentially-evaluated. (see [except.spec]/13.2
Arguably the type trait has to make such a use.
Then [except.spec]/7.3 specifies that it needs to be checked whether the default member initializers are potentially-throwing in order to check whether the implicit default constructor has a potentially-throwing exception specification.
Clang seems to then follow the idea that this requires instantiation of the default member initializer and therefore causes the compilation error because that instantiation is invalid.
The problems I see with that is:
- I don't see anything in the [temp.inst] about when default member initializers are instantiated or what that would mean exactly.
- [temp.inst]/15 speaks of the noexcept-specifier grammar construct and since the default constructor is implicit, that doesn't really work out.