Private Constructors and Aggregate Initialization: An Unexpected Interaction
In C , declaring a default constructor as private would naturally lead one to assume that the default construction of the associated type would become inaccessible. However, a peculiar behavior emerges when using brace-enclosed initialization syntax.
Consider the following example:
class C { C() = default; }; int main() { C c; // error: C::C() is private auto c2 = C(); // error: calling a private constructor }
Unexpectedly, the above code generates errors on all major compilers. This is expected behavior because the default constructor is indeed declared as private. However, using brace-enclosed initialization suddenly allows default construction:
int main() { C c{}; // OK auto c2 = C{}; // OK }
The reason behind this unexpected behavior lies in the intricacies of C standard library. According to the C 14 standard (8.4.2/5), a function is considered "user-provided" if it is user-declared and not explicitly defaulted or deleted on its first declaration.
In the case of the C class above, the default constructor is explicitly defaulted on its first declaration. This means that it is not considered "user-provided." Consequently, the C class has no user-provided constructors, which makes it an aggregate according to the definition in 8.5.1/1:
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).
As a result, brace-enclosed initialization is permitted for the C class, allowing for default construction even though the constructor is declared as private. This behavior can be surprising and could lead to undesired consequences in certain scenarios.
The above is the detailed content of Why Can I Initialize an Aggregate with a Private Default Constructor?. For more information, please follow other related articles on the PHP Chinese website!