Let's say I have a type and I want to make its default constructor private. I write the following:
class C {
C() = default;
};
int main() {
C c; // error: C::C() is private within this context (g++)
// error: calling a private constructor of class 'C' (clang++)
// error C2248: 'C::C' cannot access private member declared in class 'C' (MSVC)
auto c2 = C(); // error: as above
}
Great.
But then, the constructor turns out to not be as private as I thought it was:
class C {
C() = default;
};
int main() {
C c{}; // OK on all compilers
auto c2 = C{}; // OK on all compilers
}
This strikes me as very surprising, unexpected, and explicitly undesired behavior. Why is this OK?
You're not calling the default constructor, you're using aggregate initialization on an aggregate type. Aggregate types are allowed to have a defaulted constructor, so long as it's defaulted where it's first declared:
From [dcl.init.aggr]/1:
and from [dcl.fct.def.default]/5
Thus, our requirements for an aggregate are:
C
fulfills all of these requirements.Naturally, you may be rid of this false default construction behavior by simply providing an empty default constructor, or by defining the constructor as default after declaring it:
The trick is in C++14 8.4.2/5 [dcl.fct.def.default]:
Which means that
C
's default constructor is actually not user-provided, because it was explicitly defaulted on its first declaration. As such,C
has no user-provided constructors and is therefore an aggregate per 8.5.1/1 [dcl.init.aggr]: