隐式移动与复制操作及遏制 [英] Implicit move vs copy operations and containment

查看:141
本文介绍了隐式移动与复制操作及遏制的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当类的成员未定义移动操作时,我正在努力理解隐式移动操作:

I am struggling to understand implicit move operations when a class has a member whose move operations were not defined:

int main() {
    struct A // no move: move = copy
    {
        A() = default;
        A(const A&) {
            cout << "A'copy-ctor\n";
        };
        A& operator=(const A&) {
            cout << "A'copy-assign\n";
            return *this;
        }
    };

    struct B
    {
        B() = default;
        A a; // does this make B non-moveable?
        unique_ptr<int> upi;
        // B(B&&) noexcept = default;
        // B& operator=(B&&)noexcept = default;
    };

    A a;
    A a2 = std::move(a); // ok use copy ctor instead of move one
    a2 = std::move(a); // ok use copy assignment instead of move one

    B b;
    B b2 = std::move(b); // why this works?
    b = std::move(b2); // and this works?
    // b = b2; // error: copy deleted because of non-copyable member upi

    cout << "\nDone!\n";
}

所以我看到的是A是一个不可移动的类,因为它的复制控制操作已定义,因此只能复制它,并且任何尝试移动此类的对象的尝试都将使用相应的复制操作

So what I see is A is a non-moveable class because of the definition of its copy control operations so it can only be copied and any attempt to move an object of this class, the corresponding copy operation is used instead.

直到这里,如果我是对的,那还可以.但是B有一个不可复制的对象upi,它是unique_ptr,因此复制操作被定义为已删除的函数,因此我们无法复制此类的对象.但是此类具有不可移动的对象a,因此我认为此类(B)既不可复制,也不可移动.但是,为什么b2的初始化和b的分配工作正常?到底会发生什么?

Until here it is OK if i am correct. But B has a non-copy-able object upi which is a unique_ptr thus the copy operations are defined as deleted functions so we cannot copy objects of this class. But this class has a non-move-able object a thus i think that this class (B) is neither copy-able nor move-able. But why the initialization of b2 and the assignment of b works fine? What happens exactly?

B b2 = std::move(b); // ok?!

为什么上面的代码行调用类A的副本构造函数,并调用B的move构造函数?

Why the line above invokes the copy constructor of class A and does it invoke move constructor of B?

  • 对我来说,情况变得更糟:如果我取消注释B中的move操作行,则上面的初始化将不会编译有关引用已删除功能的抱怨,这与赋值相同!
  • Things get more worse for me: if I uncomment the lines of move operations in B, the initialization above will not compile complaining about referencing a deleted funtion, the same thing for the assignment!

任何人都可以帮助我发生什么情况吗?在这里发布问题之前,我已经Google搜索并阅读了cppreference和许多网站.

Can anyone help me what happens exactly? I have googled and read in cppreference and many websites before posting the question here.

输出:

A'copy-ctor
A'copy-assign
A'copy-ctor
A'copy-assign

Done!

推荐答案

请记住在C ++中移动"数据的含义(假设我们遵循通常的约定).如果将对象x移至对象y,则y会接收x中的所有数据,并且x是...好吧,只要我们不关心x是什么,只要它仍然可以销毁.通常,我们认为x会丢失其所有数据,但这不是必需的.唯一需要的是x是有效的.如果x最后得到的数据与y相同,则我们不在乎.

Keep in mind what it means to "move" data in C++ (assuming we follow the usual conventions). If you move object x to object y, then y receives all the data that was in x and x is... well, we don't care what x is as long as it is still valid for destruction. Often we think of x as losing all of its data, but that is not required. All that is required is that x is valid. If x ends up with the same data as y, we don't care.

x复制到y会导致y接收x中的所有数据,并且x处于有效状态(假定复制操作遵循约定并且没有错误).因此,复制算作移动.除了复制操作之外,定义移动操作的原因不是为了允许新操作,而是在某些情况下允许更高的效率.除非您采取措施防止移动,否则任何可以复制的内容都可以移动.

Copying x to y causes y to receive all the data that was in x, and x is left in a valid state (assuming the copy operation follows conventions and is not buggy). Thus, copying counts as moving. The reason for defining move operations in addition to copy operations is not to permit something new, but to permit greater efficiency in some cases. Anything that can be copied can be moved unless you take steps to prevent moves.

所以我看到的是A是一个不可移动的类,因为它的复制控制操作已定义,因此只能复制它,并且任何尝试移动此类的对象的尝试都将使用相应的复制操作

So what I see is A is a non-moveable class because of the definition of its copy control operations so it can only be copied and any attempt to move an object of this class, the corresponding copy operation is used instead.

我看到的是A是一个 moveable 类(尽管缺少move构造函数和move分配),这是因为其复制控制操作的定义.任何试图移动此类的对象都将依赖于相应的复制操作.如果要使一个类可复制但不能移动,则需要删除移动操作,同时保留复制操作. (尝试一下.将A(A&&) = delete;添加到A的定义中.)

What I see is that A is a moveable class (despite the lack of move constructor and move assignment), because of the definition of its copy control operations. Any attempt to move an object of this class will fall back on the corresponding copy operation. If you want a class to be copyable but not movable, you need to delete the move operations, while retaining the copy ones. (Try it. Add A(A&&) = delete; to your definition of A.)

B类具有一个可以移动或复制的成员,以及一个可以移动但不能复制的成员.因此,B本身可以移动,但不能复制.当移动B时,unique_ptr成员将按照您的期望进行移动,并且将复制A成员(移动类型为A的对象的后备).

The B class has one member that can be moved or copied, and one member that can be moved but not copied. So B itself can be moved but not copied. When B is moved, the unique_ptr member will be moved as you expect, and the A member will be copied (the fallback for moving objects of type A).

对我而言,情况变得更糟:如果我取消注释B中的move操作行,则上面的初始化将不会编译有关引用已删除功能的抱怨,这与赋值相同!

Things get more worse for me: if I uncomment the lines of move operations in B, the initialization above will not compile complaining about referencing a deleted funtion, the same thing for the assignment!

更仔细地阅读错误消息.当我复制此结果时,在使用已删除的函数"错误之后出现一条注释,其中提供了更多详细信息:删除了移动构造函数,因为其异常规范与隐式异常规范不匹配".删除noexcept关键字可允许代码进行编译(使用gcc 9.2和6.1).

Read the error message more closely. When I replicated this result, the "use of deleted function" error was followed by a note providing more details: the move constructor was deleted because "its exception-specification does not match the implicit exception-specification". Removing the noexcept keywords allowed the code to compile (using gcc 9.2 and 6.1).

或者,您可以将noexcept添加到副本构造函数和A的副本分配中(在B的移动操作中保留noexcept).这是一种证明B的默认移动操作使用A的复制操作的方法.

Alternatively, you could add noexcept to the copy constructor and copy assignment of A (keeping noexcept on the move operations of B). This is one way to demonstrate that the default move operations of B use the copy operations of A.

这篇关于隐式移动与复制操作及遏制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆