Angew's and jaggedSpire's' answers are excellent and apply to c++11. And c++14. And c++17.
However, in c++20, things change a bit and the example in the OP will no longer compile:
class C {
C() = default;
};
C p; // always error
auto q = C(); // always error
C r{}; // ok on C++11 thru C++17, error on C++20
auto s = C{}; // ok on C++11 thru C++17, error on C++20
As pointed out by the two answers, the reason the latter two declarations work is because C
is an aggregate and this is aggregate-initialization. However, as a result of P1008 (using a motivating example not too dissimilar from the OP), the definition of aggregate changes in C++20 to, from [dcl.init.aggr]/1:
An aggregate is an array or a class ([class]) with
- no user-declared or inherited constructors ([class.ctor]),
- no private or protected direct non-static data members ([class.access]),
- no virtual functions ([class.virtual]), and
- no virtual, private, or protected base classes ([class.mi]).
Emphasis mine. Now the requirement is no user-declared constructors, whereas it used to be (as both users cite in their answers and can be viewed historically for C++11, C++14, and C++17) no user-provided constructors. The default constructor for C
is user-declared, but not user-provided, and hence ceases to be an aggregate in C++20.
Here is another illustrative example of aggregate changes:
class A { protected: A() { }; };
struct B : A { B() = default; };
auto x = B{};
B
was not an aggregate in C++11 or C++14 because it has a base class. As a result, B{}
just invokes the default constructor (user-declared but not user-provided), which has access to A
's protected default constructor.
In C++17, as a result of P0017, aggregates were extended to allow for base classes. B
is an aggregate in C++17, which means that B{}
is aggregate-initialization that has to initialize all the subobjects - including the A
subobject. But because A
's default constructor is protected, we don't have access to it, so this initialization is ill-formed.
In C++20, because of B
's user-declared constructor, it again ceases to be an aggregate, so B{}
reverts to invoking the default constructor and this is again well-formed initialization.
C c{};
aggregate initialization so no constructor is called? – EureC
is an aggregate. – Melone=default
ctor, that would seem more reasonable. But the private=default
ctor seems like an important thing that shouldn't be ignored. What more,class C { C(); } inline C::C()=default;
being quite different is somewhat surprising. – Hooknoseprivate
to override that the compiler generated default ispublic
. – Eureprivate
does work that way. The code works because in aggregate initialization, the constructor is never called. – Keithakeithley