Why is there no default assignment / move-constructor?

I am a simple programmer. My class variables most often consist of POD types and STL containers. Because of this, I rarely have to write assignment operators or copy constructors, since they are implemented by default.

Add to this, if I use std::move for objects that are not movable, it uses the assignment operator, i.e. std::move completely safe.

Since I am a simple programmer, I would like to take advantage of the move features without adding a constructor / move assignment operator for each class I write, since the compiler could just implement them as " this->member1_ = std::move(other.member1_);... ",

But this is not the case (at least not in Visual 2010), is there any special reason for this?

More important; Is there any way around this?

Update: If you look at GManNickG's answer, it will provide an excellent macro for this. And if you don’t know, if you implement the semantics of movement, you can remove the swap member function.

+78
c ++ c ++ 11 move-semantics pod
Jan 27 '11 at 17:51
source share
4 answers

The implicit generation of constructor moves and assignment operators has been controversial and there have been significant changes in recent projects of the C ++ standard, so currently available compilers probably behave differently with respect to the implicit generation.

For more information on the history of the problem, see the list of WG21 articles for 2010 and search for "mov"

The current specification (N3225, since November) states the state (N3225 12.8 / 8):

If the definition of class X does not explicitly declare the move constructor, it will be declared as implicit as default, if and only if

  • X does not have a user-declared copy constructor, but

  • X does not have a user-declared copy destination operator,

  • X does not have a user-declared move destination operator,

  • X does not have a user-declared destructor and

  • the move constructor will not be implicitly defined as remote.

There is a similar language in 12.8 / 22 that defines when a movement assignment operator is implicitly declared as default. You can find the full list of changes made to support the current specification of implicit movement generation in N3203: Tighter conditions for creating implicit moves , which was mainly based on one of the resolutions proposed by Bjarna Straustrup's document N3201: Movement to the right .

+70
Jan 27 '11 at 18:33
source share

Implicitly created motion constructors have been considered for the standard, but can be dangerous. See Dave Abrahams analysis .

In the end, however, the standard included implicit generation of move constructors and redirection operators, albeit with a fairly significant list of restrictions:

If the definition of the class X does not explicitly declare the move constructor, it will be declared as implicit as default, if and only if - X does not have a copy constructor declared by the user,
- X does not have a user-declared copy copy operator,
- X does not have a user-defined move destination operator,
- X does not have a user-declared destructor and
- the move constructor will not be implicitly defined as remote.

That is not all that is in history. Ctor can be declared, but is still defined as remote:

The implicitly declared copy / move constructor is an inline public member of its class. By default, the copy / move constructor for class X is defined as remote (8.4.3) if X has:

is a variant member with a nontrivial corresponding constructor, and X is a joint class,
- a non-static data member of class M (or its array) that cannot be copied / moved, since the overload resolution (13.3) applicable to the corresponding constructor of Ms leads to ambiguity or a function that is deleted or inaccessible from the default constructor,
- a direct or virtual base class B that cannot be copied / moved because the overload resolution (13.3) applicable to the corresponding constructor of Bs leads to ambiguity or a function that is removed or inaccessible from the default constructor,
- any direct or virtual base class or non-static member of a data type with a destructor that is removed or inaccessible from the default constructor,
- for copy constructor, non-static data member of reference type rvalue, or
- for a move constructor, a non-static data element, or a direct or virtual base class with a type that does not have a move constructor and cannot be trivially copied.

+12
Jan 27 2018-11-17T00:
source share

(so far I'm working on a dumb macro ...)

Yes, I also went this way. Here is your macro:
 // detail/move_default.hpp #ifndef UTILITY_DETAIL_MOVE_DEFAULT_HPP #define UTILITY_DETAIL_MOVE_DEFAULT_HPP #include <boost/preprocessor.hpp> #define UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR_BASE(pR, pData, pBase) pBase(std::move(pOther)) #define UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT_BASE(pR, pData, pBase) pBase::operator=(std::move(pOther)); #define UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR(pR, pData, pMember) pMember(std::move(pOther.pMember)) #define UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT(pR, pData, pMember) pMember = std::move(pOther.pMember); #define UTILITY_MOVE_DEFAULT_DETAIL(pT, pBases, pMembers) \ pT(pT&& pOther) : \ BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM( \ UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR_BASE, BOOST_PP_EMPTY, pBases)) \ , \ BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM( \ UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR, BOOST_PP_EMPTY, pMembers)) \ {} \ \ pT& operator=(pT&& pOther) \ { \ BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT_BASE, BOOST_PP_EMPTY, pBases) \ BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT, BOOST_PP_EMPTY, pMembers) \ \ return *this; \ } #define UTILITY_MOVE_DEFAULT_BASES_DETAIL(pT, pBases) \ pT(pT&& pOther) : \ BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM( \ UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR_BASE, BOOST_PP_EMPTY, pBases)) \ {} \ \ pT& operator=(pT&& pOther) \ { \ BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT_BASE, BOOST_PP_EMPTY, pBases) \ \ return *this; \ } #define UTILITY_MOVE_DEFAULT_MEMBERS_DETAIL(pT, pMembers) \ pT(pT&& pOther) : \ BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM( \ UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR, BOOST_PP_EMPTY, pMembers)) \ {} \ \ pT& operator=(pT&& pOther) \ { \ BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT, BOOST_PP_EMPTY, pMembers) \ \ return *this; \ } #endif 

 // move_default.hpp #ifndef UTILITY_MOVE_DEFAULT_HPP #define UTILITY_MOVE_DEFAULT_HPP #include "utility/detail/move_default.hpp" // move bases and members #define UTILITY_MOVE_DEFAULT(pT, pBases, pMembers) UTILITY_MOVE_DEFAULT_DETAIL(pT, pBases, pMembers) // base only version #define UTILITY_MOVE_DEFAULT_BASES(pT, pBases) UTILITY_MOVE_DEFAULT_BASES_DETAIL(pT, pBases) // member only version #define UTILITY_MOVE_DEFAULT_MEMBERS(pT, pMembers) UTILITY_MOVE_DEFAULT_MEMBERS_DETAIL(pT, pMembers) #endif 

(I deleted the real comments, which are long and documentary.)

You specify the bases and / or members in your class as a list of preprocessors, for example:

 #include "move_default.hpp" struct foo { UTILITY_MOVE_DEFAULT_MEMBERS(foo, (x)(str)); int x; std::string str; }; struct bar : foo, baz { UTILITY_MOVE_DEFAULT_BASES(bar, (foo)(baz)); }; struct baz : bar { UTILITY_MOVE_DEFAULT(baz, (bar), (ptr)); void* ptr; }; 

And the command move-constructor and move-assign comes out.

(Aside, if anyone knows how I could combine parts into one macro, that would be swelling.)

+8
Jan 28 2018-11-11T00:
source share

VS2010 does not do this because they were not standard during implementation.

+4
Jan 27 '11 at 18:25
source share



All Articles