139

这是

struct Example { 
    string a, b; 

    Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }
    Example& operator=(Example&& mE) { a = move(mE.a); b = move(mE.b); return *this; } 
}

相当于这个

struct Example { 
    string a, b;

    Example(Example&& mE)            = default;
    Example& operator=(Example&& mE) = default;
}

?

4

4 回答 4

67

是的,两者都是一样的。

struct Example { 
    string a, b; 

    Example(Example&& mE)            = default;
    Example& operator=(Example&& mE) = default;
}

此版本将允许您跳过正文定义。

但是,您在声明时必须遵守一些规则explicitly-defaulted-functions

8.4.2 显式默认函数 [dcl.fct.def.default]

形式的函数定义:

  attribute-specifier-seqopt decl-specifier-seqopt declarator virt-specifier-seqopt = default ;

称为显式默认定义。显式默认的函数应

  • 是一个特殊的成员函数,

  • 具有相同的声明函数类型(除了可能不同的ref-qualifiers以及在复制构造函数或复制赋值运算符的情况下,参数类型可能是“对非 const 的引用T”,其中T是成员函数的名称class) 就好像它已被隐式声明,

  • 没有默认参数。

于 2013-08-17T16:11:41.770 回答
31

是的,默认的移动构造函数将执行其基和成员的成员移动,因此:

Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }

相当于:

Example(Example&& mE)                 = default;

我们可以通过C++11 标准草稿部分12.8 复制和移动类对象13段看到这一点(强调我的前进):

一个默认且未定义为已删除的复制/移动构造函数, 如果它被 odrused (3.2) 或当它在其第一次声明后显式默认时被隐式定义。[注意:复制/移动构造函数是隐式定义的,即使实现省略了它的 odr-use (3.2, 12.2)。——尾注][...]

15段说:

非联合类 X 的隐式定义的复制/移动构造函数执行其基类和成员的成员复制/移动。[注意:非静态数据成员的大括号或等号初始化器被忽略。另见 12.6.2 中的示例。—尾注] 初始化的顺序与用户定义的构造函数中基类和成员的初始化顺序相同(参见 12.6.2)。让 x 或者是构造函数的参数,或者对于移动构造函数,是一个引用参数的 xvalue。每个基本或非静态数据成员都以适合其类型的方式复制/移动:

  • 如果该成员是一个数组,则每个元素都直接使用 x 的相应子对象进行初始化;
  • 如果成员 m 具有右值引用类型 T&&,则使用 static_cast(xm) 直接初始化它;
  • 否则,基数或成员将直接用 x 的相应基数或成员初始化。

虚拟基类子对象只能由隐式定义的复制/移动构造函数初始化一次(见 12.6.2)。

于 2014-08-04T17:03:13.083 回答
29

移动构造函数是否=default等同于成员移动构造函数?

的。更新:嗯,并非总是如此。看这个例子:

#include <iostream>

struct nonmovable
{
    nonmovable() = default;

    nonmovable(const nonmovable  &) = default;
    nonmovable(      nonmovable &&) = delete;
};

struct movable
{
    movable() = default;

    movable(const movable  &) { std::cerr << "copy" << std::endl; }
    movable(      movable &&) { std::cerr << "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)); // prints copy
}

它打印:

copy

http://coliru.stacked-crooked.com/a/62c0a0aaec15b0eb

您声明了默认的移动构造函数,但发生的是复制而不是移动。为什么?因为如果一个类甚至有一个不可移动的成员,那么显式默认的移动构造函数就会被隐式删除(这样的双关语)。因此,当您运行时has_nonmovable d = std::move(c),实际上调用了复制构造函数,因为has_nonmovable(隐式)删除了移动构造函数,它只是不存在(即使您通过表达式显式声明了移动构造函数has_nonmovable(has_nonmovable &&) = default)。

但是,如果non_movable根本没有声明移动构造函数,移动构造函数将用于movable(以及对于具有移动构造函数的每个成员),而复制构造函数将用于nonmovable(以及对于未定义移动构造函数的每个成员) )。请参阅示例:

#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

http://coliru.stacked-crooked.com/a/420cc6c80ddac407

更新:但是如果您注释掉该行has_nonmovable(has_nonmovable &&) = default;,则两个成员都将使用副本: http: //coliru.stacked-crooked.com/a/171fd0ce335327cd - 打印:

movable::copy
nonmovable::copy

所以可能=default到处都是有意义的。这并不意味着您的移动表达式将始终移动,但它使这种可能性更高。

另一个更新:但是如果注释掉该行has_nonmovable(const has_nonmovable &) = default;,那么结果将是:

movable::move
nonmovable::copy

因此,如果您想知道程序中发生了什么,请自己做所有事情:sigh:

于 2018-02-26T11:38:17.383 回答
-2

除了非常病态的病例......是的。

更准确地说,您还必须考虑最终的碱基Example可能具有完全相同的规则。首先是基础 - 按声明顺序 - 然后是成员,始终按声明顺序。

于 2013-08-17T15:49:58.287 回答