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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18290523/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 21:51:27  来源:igfitidea点击:

Is a `=default` move constructor equivalent to a member-wise move constructor?

c++c++11constructordefaultmove-semantics

提问by Vittorio Romeo

Is this

这是

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; } 
}

equivalent to this

相当于这个

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;
}

?

?

采纳答案by Pierre Fourgeaud

Yes both are the same.

是的,两者都是一样的。

But

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;
}

This version will permits you to skip the body definition.

此版本将允许您跳过主体定义。

However, you have to follow some rules when you declare explicitly-defaulted-functions:

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

8.4.2 Explicitly-defaulted functions [dcl.fct.def.default]

A function definition of the form:

  attribute-speci?er-seqopt decl-speci?er-seqopt declarator virt-speci?er-seqopt = default ;

is called an explicitly-defaulteddefinition. A function that is explicitly defaulted shall

  • be a special member function,

  • have the same declared function type (except for possibly differing ref-qualifiersand except that in the case of a copy constructor or copy assignment operator, the parameter type may be “reference to non-const T”, where Tis the name of the member function's class) as if it had been implicitly declared,

  • not have default arguments.

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

形式的函数定义:

  attribute-speci?er-seqopt decl-speci?er-seqopt declarator virt-speci?er-seqopt = default ;

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

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

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

  • 没有默认参数。

回答by Shafik Yaghmour

Yes, a defaulted move constructor will perform a member-wise move of its base and members, so:

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

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

is equivalent to:

相当于:

Example(Example&& mE)                 = default;

we can see this by going to the draft C++11 standardsection 12.8Copying and moving class objectsparagraph 13which says (emphasis mine going forward):

我们可以通过转到C++11 标准草案部分12.8复制和移动类对象13段来看到这一点,其中说(强调我的未来):

A copy/move constructor that is defaulted and not defined as deleted is implicitly definedif it is odrused (3.2) or when it is explicitly defaulted after its first declaration. [ Note: The copy/move constructor is implicitly defined even if the implementation elided its odr-use (3.2, 12.2). —end note ][...]

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

and paragraph 15which says:

和第15段说:

The implicitly-defined copy/move constructorfor a non-union class X performs a memberwise copy/move of its bases and members. [ Note: brace-or-equal-initializers of non-static data members are ignored. See also the example in 12.6.2. —end note ] The order of initialization is the same as the order of initialization of bases and members in a user-defined constructor (see 12.6.2). Let x be either the parameter of the constructor or, for the move constructor, an xvalue referring to the parameter. Each base or non-static data member is copied/moved in the manner appropriate to its type:

  • if the member is an array, each element is direct-initialized with the corresponding subobject of x;
  • if a member m has rvalue reference type T&&, it is direct-initialized with static_cast(x.m);
  • otherwise, the base or member is direct-initialized with the corresponding base or member of x.

Virtual base class subobjects shall be initialized only once by the implicitly-defined copy/move constructor (see 12.6.2).

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

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

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

回答by anton_rh

Is a =defaultmove constructor equivalent to a member-wise move constructor?

是一个=default移动的构造相当于一个成员明智之举构造?

Yes. Update:Well, not always. Look at this example:

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

#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
}

It prints:

它打印:

copy

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

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

You declared defaulted move constructor, but copying happens instead of moving. Why? Because if a class has even a single non-movable member then the explicitlydefaultedmove constructor is implicitlydeleted(such a pun). So when you run has_nonmovable d = std::move(c), the copy constructor is actually called, because the move constructor of has_nonmovableis deleted (implicitly), it just doesn't exists (even though you explicitly declared the move constructor by expression has_nonmovable(has_nonmovable &&) = default).

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

But if the move constructor of non_movablewas not declared at all, the move constructor would be used for movable(and for every member that has the move constructor) and the copy constructor would be used for nonmovable(and for every member that does not define the move constructor). See the example:

但是,如果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));
}

It prints:

它打印:

movable::move
nonmovable::copy

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

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

Update:But if you comment out the line has_nonmovable(has_nonmovable &&) = default;, then copy will be used for both members: http://coliru.stacked-crooked.com/a/171fd0ce335327cd- prints:

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

movable::copy
nonmovable::copy

So probably putting =defaulteverywhere still makes sense. It doesn't mean that your move expressions will always move, but it makes chances of this higher.

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

One more update:But if comment out the line has_nonmovable(const has_nonmovable &) = default;either, then the result will be:

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

movable::move
nonmovable::copy

So if you want to know what happens in your program, just do everything by yourself :sigh:

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

回答by Emilio Garavaglia

apart very pathological cases ... YES.

除了非常病态的情况……是的。

To be more precise, you have also to considered eventual bases Examplemay have, with exact same rules. First the bases -in declaration order- then the members, always in declaration order.

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