Aggregate initialization is a special form of list initialization which can be used for aggregate types.
An aggregate is an array or a class ([class]) with
(1.1) no user-declared or inherited constructors ([class.ctor]),
(1.2) no private or protected direct non-static data members ([class.access]),
(1.3) no virtual functions ([class.virtual]), and
(1.4) no virtual, private, or protected base classes ([class.mi]).
See the C++20 standard, dcl.init.aggr#1
I'm wondering if we can test for these requirements without compiler magic.
Definitely Possible
The polymorphism part of 1.3
and 1.4
is covered by std::is_polymorphic
, which exploits the fact that dynamic_cast
is only valid for polymorphic classes.
Probably Impossible
How can we test whether out bases classes are inherited-from private
ly? And how can we do so for our data members? There are no reflections that would list all of our members or bases classes, let alone their access specifiers.
But the requirement I'm most interested in is 1.1
, which would need to detect if we have no user-declared constructors. Even an explicitly defaulted constructor is user-declared.
struct Aggregate {};
struct NonAggregate { NonAggregate() = default; };
How can we possibly distinguish Aggregate
and NonAggregate
? We would need an expression that is ill-formed for one but not for the other, then use SFINAE. However, both can be default-initialized and value-initialized. See Compiler Explorer to confirm that NonAggregate
is indeed not an aggregate type (since C++20).