Date: Sat, 12 Dec 2020 03:29:32 +0000
[dcl.init.general]/8:
> To _value-initialize_ an object of type T means:
>
> - if T is a (possibly cv-qualified) class type ([class]), then
> - if T has either no default constructor ([class.default.ctor]) or a default constructor that is user-provided or deleted, then the object is default-initialized;
> - otherwise, the object is zero-initialized and the semantic constraints for default-initialization are checked, and if T has a non-trivial default constructor, the object is default-initialized;
> - if T is an array type, then each element is value-initialized;
> - otherwise, the object is zero-initialized.
Consider
```
template<bool Cond>
struct A {
A() = default; // #1
template<class=void> A() {} // #2
A() requires Cond = delete; // #3
int x;
};
void f() {
constexpr A<false> a = {};
static_assert(a.x == 0);
}
```
Here `A` "has" a default constructor that is user-provided (#2) and one that is deleted (#3), although neither will be selected by overload resolution. Therefore, according to [dcl.init.general]/8, the variable `a` is *not* zero-initialized, and thus `a.x` has indeterminate value, which makes the static_assert invalid.
That is, #2 and #3 affect the semantics of the code, even though they are not selected.
This seems counter-intuitive and hard to learn, and disagrees with all compilers I tested (GCC/Clang/MSVC), which all accept the code, despite the existence of `#2` and `#3`.
Received on 2020-12-11 21:29:36