This is a matter of semantics in some cases. This is not very obvious with the default constructors, but it becomes obvious with other functions generated by the compiler.
For the default constructor, it would be possible for any default constructor with an empty body to be considered a candidate for a trivial constructor, as well as using =default . After all, the old empty default constructors were legal C ++.
struct S { int a; S() {}
Regardless of whether the compiler is clear that this constructor is trivial, in most cases it is irrelevant outside of optimization (manual or compiler).
However, this attempt to treat the bodies of an empty function as "default" is completely destroyed for other types of member functions. Consider the copy constructor:
struct S { int a; S() {} S(const S&) {}
In the above case, the copy constructor written with an empty body is now erroneous. He no longer copies anything. This is a completely different set of semantics than the default instance constructor semantics. The desired behavior requires you to write code:
struct S { int a; S() {} S(const S& src) : a(src.a) {}
However, even in this simple case, it becomes much harder for the compiler to verify that the copy constructor is identical to the one that he generated, or to see that the copy constructor is trivial (equivalent to a
memcpy , basically). The compiler will need to check each initializer expression of each element and make sure that it is identical to the expression for accessing the corresponding source element and no one else, make sure that none of the participants are left with a non-trivial default construction, etc. It is reversed in the compiler will use to verify that the native generated versions of this function are trivial.
Consider then the copy assignment operator, which can become even more hairy, especially in the non-trivial case. This is a ton of boiler stove that you do not want to write for many classes, but you are still forced into C ++ 03:
struct T { std::shared_ptr<int> b; T();
This is a simple case, but it already has more code than you would ever want to write for a simple type like T (especially when we throw move operations into the mix). We cannot rely on an empty body, meaning "fill in the default values," because the empty body is already excellent and has a clear meaning. In fact, if an empty body was used to mean "filling in the default values," then it would not be possible to explicitly create a constructor without copying op, etc.
This is again a matter of consistency. An empty body means "do nothing," but for things like copy constructors, you really don't want to "do nothing," but rather, "do whatever you usually do if you don't suppress." Therefore, =default . This is necessary to overcome the suppressed functions created by the compiler, such as copy / move constructors and assignment operators. Then it's just “obvious” to make it work for the default constructor as well.
It might be nice to make a default constructor with an empty body, and trivial element / base element constructors are also considered trivial, as it would be with =default , if only in some cases we could make the older code more optimal, but the lowest is the code level, based on trivial default constructors for optimizations, also depends on trivial copy constructors. If you have to go and “fix” all your old copy constructors, there really isn't much to fix all your old default constructors. It is also much clearer and clearer, using explicit =default to indicate your intentions.
There are a few more things that the member functions created by the compiler will create, and you must explicitly make changes to the support. One example is constexpr support for default constructors. It’s just smarter to use =default than to add functions with all the other special keywords and those implied by =default , and this was one of the C ++ 11 themes: making the language easier. It still has a lot of warts and feedback compromises, but it’s clear that this is a big step forward from C ++ 03 when it comes to ease of use.