【问题标题】:Why is move necessary with emplace_back in this example?在这个例子中,为什么需要使用 emplace_back 移动?
【发布时间】:2018-04-29 01:22:36
【问题描述】:

以下最小工作示例在使用选项 1 或选项 2 下的代码时编译,但在使用选项 3 下的代码时编译。我假设emplace_back() 隐式使用/调用move 构造函数,那么为什么需要显式move()?它与r-valuel-value 有什么关系吗?或者这与std::unique_ptr 需要转让所有权有关吗? (我对这些概念还是陌生的,尤其是在这种情况下。)

为了完整起见,带有push_back() 的选项4 也不会编译,除非调用move()

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

注意:我不认为这是 this question 关于将 unique_ptr 参数传递给函数的重复,即使链接问题的答案很有用,因为这是在询问定义一个unique_ptr within 一个函数,然后将它移动到一个成员vector,这样它就不会在函数结束时被破坏,此外,在这个函数中专门询问emplace_back()上下文。

此外,我认为在这种情况下给出解释会很有用,因为有时很难将解释从一种情况翻译到另一种情况。谢谢!

【问题讨论】:

标签: c++ vector move-semantics unique-ptr emplace


【解决方案1】:

我假设emplace_back() 隐式使用/调用移动构造函数

抱歉,您的假设是错误的。 emplace_back 在向量中就地构造对象,即不是从其参数中复制/移动对象,而是直接构造元素,从而避免了复制/移动构造函数。

现在,如果您使用相同(但另一个)对象构造对象,那么当然会使用复制或移动构造函数,这就是您的情况。

那么为什么需要明确的move()

因为您无法复制std::unique_ptr。基本上,emplace_back 做了类似的事情:

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

就像你这样做了:T a(std::forward&lt;Ts&gt;(args)...)(仅用于构造,它实际上并没有做同样的事情)。

现在可能更明显了一点:

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

这是否与 r-valuel-value 有关?还是这与std::unique_ptr 需要转让所有权有关?

嗯,在某种程度上,是的。 std::unique_ptr 需要明确转让所有权,这就是为什么复制被禁用而移动不是的原因(你仍然想转让所有权!复制可以在任何地方发生 - 为什么 std::auto_ptr 被弃用,然后被删除)。右值默认使用移动语义,而左值不使用。通过使用std::move,您正在执行从左值到纯右值的转换,有效地“隐藏”了您拥有左值的事实,并且编译器会很高兴地摆脱它。

【讨论】:

  • 非常感谢您的回复。将仔细检查是否有任何后续问题,但现在,我有一个非常快速而愚蠢的问题:在我的代码中,为什么我能够简单地调用 move() 而不是需要使用 @987654336 @?我从不使用using namespace stdstd 中的所有其他符号都正确需要std:: 前缀...
  • @PhysicsCodingEnthusiast 这不是一个愚蠢的问题!这是因为 pBeta 在命名空间中。编译器还会搜索函数参数的命名空间以找到它。这称为 ADL。
  • 啊,好吧。这就说得通了。谢谢你。我想,为了安全起见,包含std:: 可能是个好主意,即使在 ADL 应用时也是如此?此外,还有一个令人困惑的地方:在这个例子中,所有权的转移是什么?是 from 函数 addBetato 向量 betaVec
  • @PhysicsCodingEnthusiast 我这样做,主要是为了保持一致性。但是是安全的,因为它是一个实际的语言规则 :) 我会说这是从 pBetabetaVec 发生的(或者它的一个元素 - 我想你可以说两者),但不是来自函数,因为它与其中定义的变量的所有权没有任何关系。
  • 我最初以为它是从pBeta 转移的,但我觉得很奇怪,因为这使得unique_ptr 看起来像是从自身转移到其他东西。但我认为pBeta 更准确地说是unique_ptr 的命名存储“容器”,并且所有权正在从该“容器”转移到betaVec 的元素。
猜你喜欢
  • 1970-01-01
  • 2018-11-06
  • 2020-03-26
  • 2015-06-30
  • 1970-01-01
  • 1970-01-01
  • 2015-04-16
  • 2017-12-21
  • 2015-05-20
相关资源
最近更新 更多