New syntax "= default" in C ++ 11

I don’t understand why I need to do this.

struct S { int a; S(int aa) : a(aa) {} S() = default; }; 

Why not just say:

 S() {} // instead of S() = default; 

why introduce new syntax for this?

+113
c ++ c ++ 11
Dec 29 '13 at 19:01
source share
4 answers

By default, the default constructor is defined as the same as the default user constructor without an initialization list and an empty compound statement.

§12.1 / 6 [class.ctor] The default constructor, which by default is not defined as remote, is implicitly defined when it is odr, used to create an object of its class type or when it is explicitly defaulted after its first declaration, Implicitly defined constructor by default, it executes a set of class initializations, which will be performed by the user-written default constructor for this class without the ctor initializer (12.6.2) and an empty compound instruction. [...]

However, although both constructors will behave identically, providing an empty implementation affects some properties of the class. Providing a user-defined constructor, even if it does nothing, makes the type not cumulative, and also not trivial. If you want your class to be a collection or a trivial type (or transitivity, a POD type), then you need to use = default .

§8.5.1 / 1 [dcl.init.aggr] An aggregate is an array or class without constructors provided by the user [and ...]

§12.1 / 5 [class.ctor] The default constructor is trivial if it is not provided by the user and [...]

§9 / 6 [class] A trivial class is a class that has a trivial default constructor and [...]

To demonstrate:

 #include <type_traits> struct X { X() = default; }; struct Y { Y() { }; }; int main() { static_assert(std::is_trivial<X>::value, "X should be trivial"); static_assert(std::is_pod<X>::value, "X should be POD"); static_assert(!std::is_trivial<Y>::value, "Y should not be trivial"); static_assert(!std::is_pod<Y>::value, "Y should not be POD"); } 

In addition, an explicitly improper constructor will make it constexpr if the implicit constructor were and would also give it the same exception specification that the implicit constructor would have. In the case you pointed out, the implicit constructor would not be constexpr (since it would leave the data member uninitialized), and it would also have an empty exception specification, so there is no difference. But yes, in the general case, you can manually specify constexpr and the exception specification to match the implicit constructor.

Using = default leads to some uniformity, since it can also be used with copy / move constructors and destructors. For example, an empty copy constructor will not do the same as the default copy constructor (which will execute a member copy of its members). Using the = default (or = delete ) syntax uniformly for each of these special member functions makes your code easier to read, clearly indicating your intent.

+122
Dec 29 '13 at 19:07
source share

n2210 contains some reasons:

Default management has several problems:

  • Designer definitions are related; declaring any constructor suppresses the default constructor.
  • By default, the destructor is not suitable for polymorphic classes that require an explicit definition.
  • Once the default value is suppressed, there is no means to restore it.
  • Default implementations are often more efficient than manually described implementations.
  • Custom implementations are nontrivial, which affects the semantics of types, for example. makes a non-POD type.
  • You cannot prohibit a special member function or global operator without declaring a (non-trivial) replacement.



 type::type() = default; type::type() { x = 3; } 

In some cases, the body of the class can change without requiring a change in the definition of the member function, since by default it changes from declaring additional members.

See Rule Three becomes Rule of Five with C ++ 11? :

Note that the move operator and the move assignment operator will not be generated for the class that explicitly declares any of the other special member functions, this copy constructor and copy assignment operator will not be generated for the class that explicitly declares the move constructor or move assignment operator and that a class with an explicitly declared destructor and an implicitly defined copy constructor or an implicitly assigned copy assignment operator is deprecated

+9
Dec 29 '13 at 19:18
source share

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() {} // legal C++ }; 

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&) {} // legal, but semantically wrong }; 

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) {} // fixed }; 
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(); // the usual definitions T(const T&); T& operator=(const T& src) { if (this != &src) // not actually needed for this simple example b = src.b; // non-trivial operation return *this; }; 

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.

+6
Dec 31 '14 at 21:53
source share

I have an example that will show the difference:

 #include <iostream> using namespace std; class A { public: int x; A(){} }; class B { public: int x; B()=default; }; int main() { int x = 5; new(&x)A(); // Call for empty constructor, which does nothing cout << x << endl; new(&x)B(); // Call for default constructor + Value initialization cout << x << endl; return 0; } 

Exit:

 5 0 

As we can see, calling the empty constructor A () does not initialize the members, but B () does this.

+5
Jul 05 '19 at 3:32
source share



All Articles