C++ `=default`移动构造函数是否等同于成员移动构造函数?
这是吗 相当于C++ `=default`移动构造函数是否等同于成员移动构造函数?,c++,c++11,constructor,default,move-semantics,C++,C++11,Constructor,Default,Move Semantics,这是吗 相当于 struct Example { int a, b; Example(int mA, int mB) : a{mA}, b{mB} { } Example(const Example& mE) : a{mE.a}, b{mE.b} { } Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { } Example&am
struct Example {
int a, b;
Example(int mA, int mB) : a{mA}, b{mB} { }
Example(const Example& mE) : a{mE.a}, b{mE.b} { }
Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }
Example& operator=(const Example& mE) { a = mE.a; b = mE.b; return *this; }
Example& operator=(Example&& mE) { a = move(mE.a); b = move(mE.b); return *this; }
}
除了非常病理的病例。。。对
更准确地说,您还必须考虑
示例
可能具有的最终基础,以及完全相同的规则。首先是基础-按声明顺序-然后是成员,始终按声明顺序。是的,两者都是相同的。
但是
此版本允许您跳过主体定义
但是,在声明显式默认函数时,必须遵循一些规则:
8.4.2显式默认函数[dcl.fct.def.default]
表单的函数定义:
struct Example {
int a, b;
Example(int mA, int mB) : a{mA}, b{mB} { }
Example(const Example& mE) = default;
Example(Example&& mE) = default;
Example& operator=(const Example& mE) = default;
Example& operator=(Example&& mE) = default;
}
称为显式默认定义。明确默认的函数应
- 作为一个特殊的成员函数
- 具有相同的声明函数类型(除了可能不同的ref限定符,以及在复制构造函数或复制赋值运算符的情况下,参数类型可以是“引用非常量
”,其中T
是成员函数类的名称),就好像它是隐式声明的一样T
- 没有默认参数
是的,默认移动构造函数将对其基和成员执行成员移动,因此:
attribute-specifier-seqopt decl-specifier-seqopt declarator virt-specifier-seqopt = default ;
相当于:
Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }
我们可以通过查看12.8
复制和移动类对象一节第13段来了解这一点,该段说(重点放在后面):
默认且未定义为已删除的复制/移动构造函数
是隐式定义的,如果它是odrused(3.2)或显式定义的
在第一次声明后默认。[注:复制/移动
即使省略了实现,构造函数也是隐式定义的
其odr使用(3.2,12.2)。-结束注释][…]
第15段说:
非联合类X的隐式定义的复制/移动构造函数
以成员方式复制/移动其基础和成员。[注:
忽略非静态数据成员的大括号或相等初始值设定项。
另见12.6.2中的示例。-结束注释]顺序
初始化的顺序与基和基的初始化顺序相同
用户定义构造函数中的成员(见12.6.2)。设x为
构造函数的参数,对于移动构造函数,则为
xvalue指的是参数。每个基本或非静态数据成员
以适合其类型的方式复制/移动:
- 如果成员是数组,则每个元素都直接初始化为对应的子对象x李>
- 如果成员m具有右值引用类型T&&,则使用static_cast(x.m)直接初始化它李>
- 否则,使用x的相应基或成员直接初始化基或成员
=默认的
移动构造函数是否等同于成员移动构造函数
对更新:嗯,不总是这样。看看这个例子:
Example(Example&& mE) = default;
您声明了默认的move构造函数,但会进行复制而不是移动。为什么?因为如果一个类甚至只有一个不可移动的成员,那么显式的默认的move构造函数将被隐式地删除(例如双关语)。因此,当您运行has_nonmovable d=std::move(c)
时,实际上会调用复制构造函数,因为has_nonmovable
的移动构造函数被删除(隐式),它只是不存在(即使您通过表达式显式声明移动构造函数has_nonmovable(has_nonmovable&&)=default
)
但如果未声明非移动
的移动构造函数,则移动构造函数将用于移动
(以及具有移动构造函数的每个成员),复制构造函数将用于非移动
(以及未定义移动构造函数的每个成员)。请参见示例:
copy
更新:但是如果注释掉行,则该行具有\u非可移动性(has \u非可移动性&&)=默认值代码>,则复制将用于两个成员:-打印:
movable::move
nonmovable::copy
因此,将=default
放在任何地方仍然有意义。这并不意味着您的移动表达式将始终移动,但它使这种可能性更高
再更新一次:但如果注释掉行,则该行具有\u非可移动性(const具有\u非可移动性&)=默认值代码>其中一个,则结果将是:
movable::copy
nonmovable::copy
因此,如果你想知道你的程序中发生了什么,你可以自己做所有事情:叹气:这可能是@DieterLücking的翻版:显然不是,尽管它的主题类似,一些答案可能涵盖类似的领域。但是,我们不会把关于移动语义的每一个问题都当作彼此的重复来结束。注意,我对这个问题添加了我的答案,因为当时我正在寻找一个来自标准的引用,证明它们是等价的,而被接受的答案并没有这样做。因此,我刚刚找到了引用并添加了我的答案。我还想指出,在您的示例中,默认构造函数没有声明,而析构函数是默认的-请参见引用8.4.2的文档?C++11标准或N3690均未包含8.4.2/1中的文本“且无异常规范”。它们都在8.4.2/2中说:“只有当显式默认函数被隐式声明为constexpr
时,它才可以被声明为constexpr
,并且只有当它与隐式声明上的异常规范兼容(15.4)时,它才可能具有显式异常规范。”@Casey Good catch!我引用的是N3242。。。我把我的文件弄混了。。。我更新了我的p
#include <iostream>
struct nonmovable
{
nonmovable() = default;
nonmovable(const nonmovable &) { std::cerr << "nonmovable::copy" << std::endl; }
//nonmovable( nonmovable &&) = delete;
};
struct movable
{
movable() = default;
movable(const movable &) { std::cerr << "movable::copy" << std::endl; }
movable( movable &&) { std::cerr << "movable::move" << std::endl; }
};
struct has_nonmovable
{
movable a;
nonmovable b;
has_nonmovable() = default;
has_nonmovable(const has_nonmovable &) = default;
has_nonmovable( has_nonmovable &&) = default;
};
int main()
{
has_nonmovable c;
has_nonmovable d(std::move(c));
}
movable::move
nonmovable::copy
movable::copy
nonmovable::copy
movable::move
nonmovable::copy