为什么没有默认的移动赋值/移动构造函数?
我是一个简单的程序员。 我的类成员variables通常由PODtypes和STL容器组成。 正因为如此,我很less必须编写赋值操作符或复制构造函数,因为这些都是默认实现的。
std::move
,如果我使用std::move
不可移动的对象,它会使用赋值运算符,这意味着std::move
是完全安全的。
因为我是一个简单的程序员,所以我想利用移动function,而不必为我编写的每个类添加移动构造器/赋值运算符,因为编译器可以简单地将它们实现为“ this->member1_ = std::move(other.member1_);...
“
但它不(至less在Visual 2010中),是否有任何特别的原因呢?
更重要的是; 有什么办法可以解决这个问题吗?
更新:如果你看低于GManNickG的答案,他提供了一个伟大的macros。 如果你不知道,如果你实现移动语义,你可以删除交换成员函数。
移动构造函数和赋值操作符的隐式生成一直存在争议,并且在最近的C ++标准草案中进行了重大修改,所以目前可用的编译器在隐式生成方面可能会有不同的performance。
有关该问题的详细信息,请参阅2010年WG21论文列表并search“mov”
目前的规格(N3225,从十一月)表示(N3225 12.8 / 8):
如果类
X
的定义没有明确声明一个移动构造函数,那么当且仅当
X
没有用户声明的拷贝构造函数,而且
X
没有用户声明的复制赋值操作符,
X
没有用户声明的移动赋值操作符,
X
没有用户声明的析构函数,并且移动构造函数不会被隐式定义为删除。
在12.8 / 22中有类似的语言,规定何时移动赋值运算符被隐式声明为默认值。 您可以在N3203中find为支持隐式移动生成的当前规范而进行的更改的完整列表:收紧生成隐式移动的条件 ,主要基于Bjarne Stroustrup的论文N3201提出的其中一个解决scheme:右移 。
隐式生成的移动构造函数已经被认为是标准的,但可能是危险的。 请参阅Dave Abrahams的分析 。
然而,最终的标准确实包括了隐式生成移动构造函数和移动赋值操作符,尽pipe有一个相当多的限制:
如果类X的定义没有明确声明一个移动构造函数,那么当且仅当
– X没有用户声明的拷贝构造函数,
– X没有用户声明的复制赋值操作符,
– X没有用户声明的移动赋值操作符,
– X没有用户声明的析构函数,并且
– 移动构造函数不会被隐式定义为删除。
尽pipe这个故事还不完全。 ctor可以被声明,但是仍然被定义为被删除:
隐式声明的复制/移动构造函数是其类的内联公共成员。 X的缺省拷贝/移动构造函数被定义为删除(8.4.3),如果X有:
– 一个不平凡的对应构造函数的变体成员,X是一个类似于联合体的类,
– types为M(或其数组)的非静态数据成员不能复制/移动,因为应用于M的相应构造函数的重载parsing(13.3)会导致模糊或函数被删除或无法从默认的构造函数,
– 直接的或虚拟的基类B不能复制/移动,因为应用于B的相应构造函数的重载parsing(13.3)会导致从默认的构造函数中删除或不可访问的歧义或函数,
– 具有从默认构造函数中删除或不可访问的析构函数的types的任何直接或虚拟基类或非静态数据成员,
– 对于拷贝构造函数,右值引用types的非静态数据成员,或者
– 对于移动构造函数,非静态数据成员或直接或虚拟基类,其types不具有移动构造函数,并且不是可以复制的。
(就目前而言,我正在做一个愚蠢的macros…)
是的,我也走了这条路。 这是你的macros:
// 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
(我删除了真实的评论,这是长度和纪录片。)
您可以将类中的基础和/或成员指定为预处理器列表,例如:
#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; };
然后出来一个移动构造和移动赋值运算符。
(顺便说一句,如果有人知道如何将细节合并成一个macros,那就会膨胀起来。)
VS2010没有这样做,因为它们在实施时并不是标准的。