If agg_t1's private data member x makes it non-aggregate type, then why the agg_t1 type is permitted in constexpr function definition of ce1?
C++20
agg_t1 is indeed not an aggregate class due to its private data member, however whilst aggregateness can be one of the sufficient requirements for class type to be a literal type, it is not a necessary one. Note the or condition of [basic.types.general]/10.5.2 [emphasis mine]:
A type is a literal type if it is:
- [...]
- a possibly cv-qualified class type that has all of the following properties:
- [...]
- it is either a closure type ([expr.prim.lambda.closure]), an aggregate type ([dcl.init.aggr]), or has at least one constexpr
constructor or constructor template (possibly inherited
([namespace.udecl]) from a base class) that is not a copy or move
constructor, [...]
As per [class.default.ctor]4:
[...] If that user-written default constructor would satisfy the
requirements of a constexpr constructor ([dcl.constexpr]), the
implicitly-defined default constructor is constexpr [...]
and [dcl.constexpr]/3 and [dcl.constexpr]/4:
/3 The definition of a constexpr function shall satisfy the following
requirements:
- [...]
- if the function is a constructor or destructor, its class shall not have any virtual base classes; [...]
/4 The definition of a constexpr constructor whose function-body is
not = delete shall additionally satisfy the following requirements:
- for a non-delegating constructor, every constructor selected to initialize non-static data members and base class subobjects shall
be a constexpr constructor;
- [...]
the implicitly-defined default constructor of agg_t1 is constexpr, and thus [basic.types.general]/10.5.2 does not disqualify agg_t1 from being a literal type in C++20.
C++17
In C++17 the implicitly-defined default constructor of agg_t1 is not constexpr, as it violates [dcl.constexpr]/4.5:
The definition of a constexpr constructor shall satisfy the following
constraints:
In addition, either its function-body shall be = delete, or it shall
satisfy the following constraints:
- [...]
- /4.5 every non-variant non-static data member and base class sub-object shall be initialized ([class.base.init]);
And indeed, whilst the following is rejected by both Clang and GCC for -std=c++17:
class A {
constexpr A() = default; // error: cannot be constexpr
private:
int x;
};
the following is accepted:
// OK: B is a literal type.
class B {
constexpr B() = default; // OK
private:
int x{};
};
agg_t1noragg_t2are aggregates, despite their cleverly-chosen names. You have yet to eliminate the other possibilities by which they might be literal types (closure type and whatever you cut off after the "or" -- as with the "or" operator, proving that one operand to||is false does not make the entire expression false.)agg_t1isconstexpr, per [class.default.ctor]/4-std=c++20switch, then your test compiles.