什么构成了“从其移动”的有效状态,对象在C ++ 11? [英] What constitutes a valid state for a "moved from" object in C++11?

查看:123
本文介绍了什么构成了“从其移动”的有效状态,对象在C ++ 11?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在试图围绕C ++ 11中的移动语义如何工作,而且我很难理解移动对象需要满足的条件。查看这里的答案不能真正解决我的问题,因为不能看到如何以明智的方式将它应用于pimpl对象,尽管移动语义是完美的pimpls < a>。

I've been trying to wrap my head around how move semantics in C++11 are supposed to work, and I'm having a good deal of trouble understanding what conditions a moved-from object needs to satisfy. Looking at the answer here doesn't really resolve my question, because can't see how to apply it to pimpl objects in a sensible way, despite arguments that move semantics are perfect for pimpls.

我的问题的最简单的例子涉及pimpl成语,如下:

The easiest illustration of my problem involves the pimpl idiom, like so:

class Foo {
    std::unique_ptr<FooImpl> impl_;
public:
    // Inlining FooImpl's constructors for brevity's sake; otherwise it 
    // defeats the point.
    Foo() : impl_(new FooImpl()) {}

    Foo(const Foo & rhs) : impl_(new FooImpl(*rhs.impl_)) {}

    Foo(Foo && rhs) : impl_(std::move(rhs.impl_)) {}

    Foo & operator=(Foo rhs) 
    {
        std::swap(impl_, rhs.impl_);

        return *this;
    }

    void do_stuff () 
    {
        impl_->do_stuff;
    }
};

现在,当我从 Foo ?我可以安全地销毁移动对象,我可以分配给它,两者都是绝对至关重要的。但是,如果我尝试使用 Foo 尝试 do_stuff ,它会爆炸。在我为 Foo 的定义添加move语义之前,每个 Foo 满足不变式可以 do_stuff ,情况就不一样了。似乎也没有很多很好的选择,因为(例如)将移动从 Foo 将涉及一个新的动态分配,这部分违反了目的移动语义。我可以检查 impl _ do_stuff 并初始化为默认 FooImpl 如果是,但是增加了一个(通常是假的)检查,如果我有很多方法,这将意味着记住在每一个检查。

Now, what can I do once I've moved from a Foo? I can destroy the moved-from object safely, and I can assign to it, both of which are absolutely crucial. However, if I try to do_stuff with my Foo, it will explode. Before I added move semantics for my definition of Foo, every Foo satisfied the invariant that it could do_stuff, and that's no longer the case. There don't seem to be many great alternatives, either, since (for example) putting the moved-from Foo would involve a new dynamic allocation, which partially defeats the purpose of move semantics. I could check whether impl_ in do_stuff and initialize it to a default FooImpl if it is, but that adds a (usually spurious) check, and if I have a lot of methods it would mean remembering to do the check in every one.

我应该放弃这个想法,能够 do_stuff 是一个合理的不变式吗?

Should I just give up on the idea that being able to do_stuff is a reasonable invariant?

推荐答案

您为类型定义和记录什么是有效状态,以及对类型的移动对象可以执行的操作。

You define and document for your types what a 'valid' state is and what operation can be performed on moved-from objects of your types.

移动标准库类型的对象会将对象置于未指定的状态,可以正常查询,以确定有效的操作。

Moving an object of a standard library type puts the object into an unspecified state, which can be queried as normal to determine valid operations.


17.6.5.15库类型的移动状态            ;                         ;  
[lib.types.movedfrom]

在C ++标准库中定义的类型对象可以从
(12.8) 。移动操作可以显式指定或隐式生成
。除非另有说明,否则这种移动对象将
置于有效但未指定的状态。

Objects of types defined in the C++ standard library may be moved from (12.8). Move operations may be explicitly specified or implicitly generated. Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state.

'有效'状态意味着标准为类型指定的所有要求仍然为真。这意味着您可以对移动的标准库类型使用任何操作,前提条件为true。

The object being in a 'valid' state means that all the requirements the standard specifies for the type still hold true. That means you can use any operation on a moved-from, standard library type for which the preconditions hold true.

通常,对象的状态是已知的,不得不检查它是否满足您要执行的每个操作的前提条件。与移动对象的唯一区别是,你不知道状态,所以你必须检查。例如,您不应该对移动的字符串使用pop_back(),直到您查询字符串的状态,以确定满足pop_back()的前提条件。

Normally the state of an object is known so you don't have to check if it meets the preconditions for each operation you want to perform. The only difference with moved-from objects is that you don't know the state, so you do have to check. For example, you should not pop_back() on a moved-from string until you have queried the state of the string to determine that the preconditions of pop_back() are met.

std::string s = "foo";
std::string t(std::move(s));
if (!s.empty()) // empty has no preconditions, so it's safe to call on moved-from objects
    s.pop_back(); // after verifying that the preconditions are met, pop_back is safe to call on moved-from objects

状态可能未指定,因为为标准库的所有不同实现创建一个有用的需求集将非常繁重。

The state is probably unspecified because it would be onerous to create a single useful set of requirements for all different implementations of the standard library.

由于您不仅负责规范,还负责实现您的类型,因此您可以简单地指定状态并避免查询。例如,指定从您的pimpl类型对象移动导致do_stuff成为具有未定义行为的无效操作(通过解除引用空指针)是完全合理的。该语言被设计为使得只有当不可能对移动的对象做任何事情时或者当用户非常明显地且非常明确地指示移动操作时,移动才发生,因此用户不应该被移动 - 从对象。

Since you are responsible not only for the specification but also the implementation of your types, you can simply specify the state and obviate the need for querying. For example it would be perfectly reasonable to specify that moving from your pimpl type object causes do_stuff to become an invalid operation with undefined behavior (via dereferencing a null pointer). The language is designed such that moving only occurs either when it's not possible to do anything to the moved-from object, or when the user has very obviously and very explicitly indicated a move operation, so a user should never be surprised by a moved-from object.

另请注意,标准库定义的概念不允许移动对象。这意味着为了满足标准库定义的任何概念的要求,类型的移出对象必须仍然满足概念要求。这意味着如果你的类型的对象不保持在一个有效的状态(由相关的概念定义),那么你不能使用它与标准库(或结果是未定义的行为)。

Also note that the 'concepts' defined by the standard library do not make any allowances for moved-from objects. That means that in order to meet the requirements for any of the concepts defined by the standard library, moved-from objects of your types must still fulfill the concept requirements. This means that if objects of your type don't remain in a valid state (as defined by the relevant concept) then you cannot use it with the standard library (or the result is undefined behavior).

这篇关于什么构成了“从其移动”的有效状态,对象在C ++ 11?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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