C++ 为什么没有默认的移动分配/移动构造函数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4819936/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why no default move-assignment/move-constructor?
提问by Viktor Sehr
I'm a simple programmer. My class members variables most often consists of POD-types and STL-containers. Because of this I seldom have to write assignment operators or copy constructors, as these are implemented by default.
我是一个简单的程序员。我的类成员变量通常由 POD 类型和 STL 容器组成。因此,我很少需要编写赋值运算符或复制构造函数,因为它们是默认实现的。
Add to this, if I use std::move
on objects not movable, it utilizes the assignment-operator, meaning std::move
is perfectly safe.
除此之外,如果我std::move
在不可移动的对象上使用,它会使用赋值运算符,这意味着std::move
非常安全。
As I'm a simple programmer, I'd like to take advantage of the move-capabilities without adding a move constructor/assignment operator to every class I write, as the compiler could simply implemented them as "this->member1_ = std::move(other.member1_);...
"
由于我是一个简单的程序员,我想利用移动功能的优势,而无需向我编写的每个类添加移动构造函数/赋值运算符,因为编译器可以简单地将它们实现为“ this->member1_ = std::move(other.member1_);...
”
But it doesn't (at least not in Visual 2010), is there any particular reason for this?
但它没有(至少在 Visual 2010 中没有),这有什么特别的原因吗?
More importantly; is there any way to get around this?
更重要的是; 有什么办法可以解决这个问题吗?
Update:If you look down at GManNickG's answer he provides a great macro for this. And if you didn't know, if you implement move-semantics you can remove the swap member function.
更新:如果你看不起 GManNickG 的回答,他为此提供了一个很棒的宏。如果您不知道,如果您实现移动语义,您可以删除交换成员函数。
采纳答案by James McNellis
The implicit generation of move constructors and assignment operators has been contentious and there have been major revisions in recent drafts of the C++ Standard, so currently available compilers will likely behave differently with respect to implicit generation.
移动构造函数和赋值运算符的隐式生成一直存在争议,并且在 C++ 标准的最新草案中进行了重大修订,因此当前可用的编译器在隐式生成方面的行为可能会有所不同。
For more about the history of the issue, see the 2010 WG21 papers listand search for "mov"
有关该问题历史的更多信息,请参阅2010 WG21 论文列表并搜索“mov”
The current specification (N3225, from November) states (N3225 12.8/8):
当前规范(N3225,来自 11 月)指出 (N3225 12.8/8):
If the definition of a class
X
does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if
X
does not have a user-declared copy constructor, and
X
does not have a user-declared copy assignment operator,
X
does not have a user-declared move assignment operator,
X
does not have a user-declared destructor, andthe move constructor would not be implicitly defined as deleted.
如果类的定义
X
没有显式声明移动构造函数,则当且仅当
X
没有用户声明的复制构造函数,并且
X
没有用户声明的复制赋值运算符,
X
没有用户声明的移动赋值运算符,
X
没有用户声明的析构函数,并且移动构造函数不会被隐式定义为已删除。
There is similar language in 12.8/22 specifying when the move assignment operator is implicitly declared as defaulted. You can find the complete list of changes made to support the current specification of implicit move generation in N3203: Tightening the conditions for generating implicit moves , which was based largely on one of the resolutions proposed by Bjarne Stroustrup's paper N3201: Moving right along.
12.8/22 中有类似的语言指定何时将移动赋值运算符隐式声明为默认值。您可以在N3203 中找到为支持当前隐式移动生成规范所做的更改的完整列表:收紧生成隐式移动的条件 ,这主要基于 Bjarne Stroustrup 的论文N3201:向右移动提出的决议之一。
回答by Jerry Coffin
Implicitly generated move constructors have been considered for the standard, but can be dangerous. See Dave Abrahams's analysis.
隐式生成的移动构造函数已被考虑用于标准,但可能是危险的。请参阅 Dave Abrahams 的分析。
In the end, however, the standard did include implicit generation of move constructors and move assignment operators, though with a fairly substantial list of limitations:
然而,最终,该标准确实包含了移动构造函数和移动赋值运算符的隐式生成,尽管有相当多的限制:
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if
— X does not have a user-declared copy constructor,
— X does not have a user-declared copy assignment operator,
— X does not have a user-declared move assignment operator,
— X does not have a user-declared destructor, and
— the move constructor would not be implicitly defined as deleted.
如果类 X 的定义未显式声明移动构造函数,则当且仅当
— X 没有用户声明的复制构造函数,
— X 没有用户声明的复制赋值运算符时,将隐式声明为默认值,
— X 没有用户声明的移动赋值运算符,
— X 没有用户声明的析构函数,并且
— 移动构造函数不会被隐式定义为已删除。
That's not quite all there is to the story though. A ctor can be declared, but still defined as deleted:
但这还不是故事的全部。可以声明一个ctor,但仍定义为已删除:
An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/move constructor for a class X is defined as deleted (8.4.3) if X has:
— a variant member with a non-trivial corresponding constructor and X is a union-like class,
— a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied to M's corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
— a direct or virtual base class B that cannot be copied/moved because overload resolution (13.3), as applied to B's corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
— any direct or virtual base class or non-static data member of a type with a destructor that is deleted or inaccessible from the defaulted constructor,
— for the copy constructor, a non-static data member of rvalue reference type, or
— for the move constructor, a non-static data member or direct or virtual base class with a type that does not have a move constructor and is not trivially copyable.
隐式声明的复制/移动构造函数是其类的内联公共成员。如果 X 具有:
— 具有非平凡对应构造函数和 X 的变体成员是类似联合的类,
— 由于重载决议(13.3)而无法复制/移动的类类型 M(或其数组)的非静态数据成员,如应用于 M 的相应构造函数,导致歧义或函数被删除或无法从默认构造函数中访问,
- 由于重载决议(13.3)而无法复制/移动的直接或虚拟基类 B,如应用于 B 的相应构造函数, 导致歧义或函数从默认构造函数中删除或无法访问,
— 任何直接或虚拟基类或具有从默认构造函数中删除或无法访问的析构函数的类型的非静态数据成员,
— 对于复制构造函数,右值引用类型的非静态数据成员,或
— 对于移动构造函数,非静态数据成员或具有类型没有移动构造函数且非平凡的直接或虚拟基类可复制。
回答by GManNickG
(as for now, I'm working on a stupid macro...)
(至于现在,我正在研究一个愚蠢的宏......)
Yeah, I went that route too. Here's 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've removed the real comments, which are length and documentary.)
(我已经删除了真实的评论,即长度和纪录片。)
You specify the bases and/or members in your class as a preprocessor list, 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 out comes a move-constructor and move-assignment operator.
然后是移动构造函数和移动赋值运算符。
(As an aside, if anyone knows how I could combine the details into one macro, that would be swell.)
(顺便说一句,如果有人知道我如何将细节组合成一个宏,那就太好了。)
回答by Puppy
VS2010 doesn't do it because they weren't Standard at the time of implementation.
VS2010 没有这样做,因为它们在实施时不是标准的。