How C ++ 11 implements "... = default;" for the rule of three methods

When I found out that C ++ people told me that they always implement at least a rule of three methods.

Now I see the new "... = default;" from C ++ 0x on stack overflow, and my question is:

Is there a standard C ++ 11 implementation for these methods, or is it specific to the compiler?

plus I would like to have some recommendations:

  • What does the implementation look like in terms of code? (if it is common)
  • Does this have an advantage over my version below?
  • If you do not use the assignment / copy constructor, what does *... = delete* exactly, what is the difference with declaring them private? Reply (from @ 40two)
  • Is the new default = different from the old default implementation?

Disclaimer: when I need more advanced functions in my methods, I will certainly implement them myself. But I'm used to implementing the assignment operator and copy constructor, even when I never used them, just to prevent the compiler from doing this.


What I used: (edited, @DDrmmr swap / move )

 //File Th class T { public: T(void); T(const T &other); T(const T &&other); T &operator=(T other); friend void swap(T &first, T &second); ~T(void); protected: int *_param; }; //File T.cpp T::T(void) : _param(std::null) {} T::T(T &other) : _param(other._param) {} T::T(T &&other) : T() { swap(*this, other); } T &T::operator=(T other) { swap(*this, other); return (*this); } friend void swap(T &first, T &second) { using std::swap; swap(first._param, second._param); } T::~T(void) {} 
+7
c ++ c ++ 11
source share
1 answer

Default behavior:

  • By default, ctor ( T() ): calls base def. ctors and default members.
  • Copy ctor ( T(const T&) ): base calls are copied. ctors and members copy ctors.
  • Move ctor ( T(T&&) ): the base switches. ctors and members move ctors.
  • Assign ( T& operator=(const T&) ): the call assigns the base. and members appoint.
  • Transfer ( T& operator=(T&&) ): forwarding databases and transferring members.
  • Destructor ( ~T() ): calls the member destructor and the database descriptor (reverse order).

For built-in types (int, etc.)

  • By default, ctor: is set to 0 if explicitly specified
  • Copy ctor: bitwise copy
  • Move ctor: bitwise copy (no change to source)
  • Assign: Bitmap
  • Transmission: Bit Copy
  • Destructor: does nothing.

Since pointers are also built-in types, this applies to int* (not what it points to).

Now, if you do not declare anything, your class T will simply contain an int * that does not own the specified int, so the copy of T just holds the pointer to the same int. This is the same as the behavior of C ++ 03. By default, movement is implemented for built-in types - copying. For classes, move in order (and depends on which members: just copies for built-in modules)

If you need to change this behavior, you must do it in a consistent manner: for example, if you want to “own” what you are pointing to, you need to

  • initializing ctor to nullptr by default: this defines an "empty state", which we can reference later
  • ctor creator initializing a given pointer
  • copy of ctor initializing copy of pointer (this is a real change)
  • dtor that removes sharp
  • an appointment that removes the pointed ones and gets a new copy of the pointed

.

 T::T() :_param() {} T::T(int* s) :_param(s) {} T(const T& s) :_param(s._param? new int(*s._param): nullptr) {} ~T() { delete _param; } // will do nothing if _param is nullptr 

Until we determine the purpose, but focus on the movement: If you do not declare it, since you announced the copy, it will be deleted: this makes the T object always copied, even if temporary (the same behavior as C ++ 03)

But if the source object is temporary, we can create an empty destination and change it:

 T::T(T&& s) :T() { std::swap(_param, s._param); } 

This is what is called movement.

Now assignment: before C ++ 11 T& operator=(const T& s) should check the self-assignment, make the destination empty and get a copy of the pointer:

 T& operator=(const T& s) { if(this == &s) return *this; // we can shortcut int* p = new int(s._param); //get the copy ... delete _param; //.. and if succeeded (no exception while copying) ... _param = p; // ... delete the old and keep the copy return *this; } 

With C ++ 11, we can use the passed parameter to generate a copy, thereby giving

 T& operator=(T s) //note the signature { std::swap(_param, s._param); return *this; } 

Note that this also works in C ++ 98, but the pass-by copy will not be optimized on transition if s is temporary. This makes such an implementation disadvantageous in C ++ 98 and C ++ 03, but very convenient in C ++ 11.

Note that there is no need to specialize std::swap for T: std::swap(a,b); will work by performing three movements (without copying)

The practice of implementing the swap function is derived for the case when T has many members, being a mandatory replacement for both movement and destination. But it can be a regular private member function.

+4
source share

All Articles