简体   繁体   English

在这个例子中为什么需要使用emplace_back进行移动?

[英]Why is move necessary with emplace_back in this example?

The following minimal working example compiles when the code under option 1 or option 2 is used, but does not compile when the code under option 3 is used. 当使用选项1或选项2下的代码时,以下最小工作示例进行编译,但在使用选项3下的代码时编译。 I was under the assumption that emplace_back() implicitly uses/calls a move constructor, so why is an explicit move() necessary? 我假设emplace_back()隐式使用/调用move构造函数,那么为什么需要显式move()呢? Does it have something to do with r-value vs. l-value ? 它与r-valuel-value吗? Or does this have to do with std::unique_ptr needing to transfer ownership? 或者这与std::unique_ptr需要转移所有权有关吗? (I am still new to these concepts, particularly in this context.) (我仍然对这些概念不熟悉,特别是在这种情况下。)

For completeness, option 4 with push_back() does not compile, either, unless move() is called. 为了完整性,除非调用move() ,否则带有push_back()选项4也不会编译。

#include <iostream>
#include <vector>
#include <memory>

class Beta {
public:
    Beta(int x, int y, int z): mX(x), mY(y), mZ(z) { };
    int mX; int mY; int mZ;

};

class Alpha {
public:
    std::vector<std::unique_ptr<Beta>> betaVec;
    void addBeta(int x, int y, int z) {
        // only choose one of the following options:
        // option 1 (compiles)
        std::unique_ptr<Beta> pBeta = std::make_unique<Beta>(x, y, z);
        betaVec.emplace_back(move(pBeta));

        // option 2 (compiles)
        betaVec.emplace_back(std::make_unique<Beta>(x, y, z));

        // option 3 (does not compile)
        std::unique_ptr<Beta> pBeta = std::make_unique<Beta>(x, y, z);
        betaVec.emplace_back(pBeta);

        // option 4 (does not compile)
        std::unique_ptr<Beta> pBeta = std::make_unique<Beta>(x, y, z);
        betaVec.push_back(pBeta);

        // option 5 (compiles)
        std::unique_ptr<Beta> pBeta = std::make_unique<Beta>(x, y, z);
        betaVec.push_back(move(pBeta));
    }
};

int main() {

    return 0;
}

Note: I do not believe that this is a duplicate of this question about passing unique_ptr parameters to functions, even though the answers to the linked question are useful, as this is asking about defining a unique_ptr within a function and then moving it to a member vector so that it is not destroyed at the end of the function and, furthermore, is asking specifically about emplace_back() in this context. 注意:我不认为这是关于将unique_ptr参数传递给函数的这个问题的重复,即使链接问题的答案很有用,因为这要求函数中定义unique_ptr然后将其移动到成员vector以便它不会在函数结束时被销毁,而且,在此上下文中具体询问emplace_back()

Additionally, I think that it would be useful to have explanations given in this context, as it is sometimes difficult to translate explanations from one context to another. 另外,我认为在这种情况下给出解释是有用的,因为有时很难将解释从一个上下文转换到另一个上下文。 Thank you! 谢谢!

I was under the assumption that emplace_back() implicitly uses/calls a move constructor 我假设emplace_back()隐式使用/调用移动构造函数

Sorry, but you're assumption is wrong. 对不起,但你的假设是错的。 emplace_back constructs the object in the vector in-place, ie instead of copying/moving the object from its parameters, it constructs the element directly which avoids the copy/move constructor. emplace_back构造了向量中的对象,即不是从对象中复制/移动对象,而是直接构造元素,避免了复制/移动构造函数。

Now, if you construct the object with the same (but another) object, then of course either the copy or the move constructor will get used instead, which is what is happening in your case. 现在,如果您使用相同(但另一个)对象构造对象,那么当然要使用复制或移动构造函数,这就是您的情况。

so why is an explicit move() necessary 那么为什么需要一个明确的move()

Because you can't copy a std::unique_ptr . 因为你无法复制std::unique_ptr Basically, emplace_back does something akin to this: 基本上, emplace_back做了类似于此的事情:

new (place) T(std::forward<Ts>(args)...);

It's like if you did: T a(std::forward<Ts>(args)...) (for construction only, it doesn't actually do the same thing). 就像你做的那样: T a(std::forward<Ts>(args)...) (仅用于构造,它实际上并没有做同样的事情)。

Now it might be a bit more obvious: 现在它可能更明显一点:

T option1(std::move(pBeta)); // ok, move
T option3(pBeta); // error, copy

Does it have something to do with r-value vs. l-value ? 它与r-valuel-value吗? Or does this have to do with std::unique_ptr needing to transfer ownership? 或者这与std::unique_ptr需要转移所有权有关吗?

Well, in a way, yes. 那么,在某种程度上,是的。 std::unique_ptr requires explicit transfer of ownership, that's why the copy is disabled and the move is not (you still want to transfer ownership! And a copy can happen everywhere - why std::auto_ptr was deprecated, then removed). std::unique_ptr需要明确的所有权转移,这就是为什么副本被禁用而移动不是(你仍然希望转移所有权!并且副本可以在任何地方发生 - 为什么std::auto_ptr被弃用,然后被删除)。 An rvalue uses move semantics by default, while an lvalue does not. 右值默认使用移动语义,而左值则不使用移动语义。 By using std::move , you are doing a conversion from an lvalue to a prvalue, effectively "hiding" the fact that you have an lvalue, and the compiler will happily move from it. 通过使用std::move ,您正在进行从左值到初值的转换,有效地“隐藏”您拥有左值的事实,编译器将很乐意从它移动。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM