【问题标题】:Does std::vector.push_back(std::move(foo)) make sense?std::vector.push_back(std::move(foo)) 有意义吗?
【发布时间】:2018-10-18 00:39:54
【问题描述】:

我在一些代码中遇到过这种情况(为清楚起见,省略了细节):

std::vector<std::vector<int>> foo;
{
    std::vector<int> bar = {42};
    foo.push_back(std::move(bar)); // Hmmm...
} // Indicate `bar` is no longer needed.

std::move 对我来说似乎没有必要,但不是吗?这种行为与foo.push_back(bar); 有什么不同吗?如果元素不是int,而是像我的实际代码中的pcl::PointXYZ 这样的类怎么办?

更新:我修改了代码,更明确地指出barstd::move之后没有使用,所以不存在非法访问等风险。

【问题讨论】:

    标签: c++11 stl move-semantics


    【解决方案1】:

    vector 类有两个 push_back 实现:

    void push_back( const T& value );
    void push_back( T&& value );
    

    第一个复制给定的元素。

    第二个尝试通过调用元素的移动构造函数(如果已定义)来“移动”它。

    使用move 强制选择第二个实现,该实现应该重用该值,而不仅仅是复制一个。

    在这种特殊情况下,会发生这种情况:

    1. 向量 bar 分配在堆栈上,但其元素 (42) 分配在堆上。
    2. 当您调用foo.push_back(...) 时,foo 在堆上分配一个新向量,这将是bars 的副本。我们称它为baz :) 根据调用push_back 的实现,将发生以下情况:
      • void push_back( const T&amp; value );:在这种情况下,bar 的所有元素也将被复制到 baz
      • void push_back( T&amp;&amp; value ); 在这种情况下baz 将接收指向bar 元素的指针,因此不会执行复制操作。但了解bar 将被剥夺其元素(现在baz 拥有它们)至关重要,因此bar 不应在move 之后使用。

    什么类型的元素并不重要(纯整数或pcl::PointXYZ),因为只有第一个向量为元素分配了内存,并且指向该内存的指针是唯一被复制的东西在move 通话期间。

    【讨论】:

    • 所以答案是肯定的,这是必要的(对于合适的“必要”值),因为是的,行为是不同的,因为我们应该保存包含42 的内存副本。但是,我是否正确地说允许智能编译器并且可以找出优化,但是std::move 明确表示必须进行优化?
    • @KenY-N,是的。它不仅使它明确,而且使它起作用。 (如果没有std::move,“优化”就不会发生)。你甚至可以使用emplacefoo.empace(std::move(bar));。如果代码那么简单(在push_back 之前没有使用var)你可以foo.emplace(std::vector{42}) 甚至foo.emplace({42})
    • @alfC ..我猜,在大多数情况下var 的使用可能会推迟到emplace 之后,因为emplace 是为了返回迭代器?
    【解决方案2】:

    std::move 对我来说似乎没有必要,但它是吗?

    这取决于你的意图。

    这种行为与 foo.push_back(bar); 有什么不同吗?

    是的,foo.push_back(bar); 会将bar 复制到foo 中(可能会降低性能,因为std::vector 处理动态分配)。这也会使bar 保持不变,然后您可以使用它。

    另一方面,foo.push_back(std::move(bar)); 不复制并重用bar 中已分配的内存。请注意,这会使 bar 在移动后处于有效但未指定的状态(也就是说,除非您重新初始化/重新分配它,否则您无法使用它)。

    如果元素不是 int 而是一个类,例如 pcl::PointXYZ 就像在我的实际代码中一样?

    移动语义仅对使用动态分配(拥有指针)的类类型有用。 pcl::PointXYZint 没有这样的类,所以 std::moveintstd::movepcl::PointXYZ 是没有意义的。

    【讨论】:

    • 注意,我要移动的实际上是int 中的std::vector
    • @KenY-N 没关系。内存仍分配给int。在这方面,移动 std::vector&lt;int&gt; 是有意义的。
    猜你喜欢
    • 2016-07-08
    • 2011-07-28
    • 1970-01-01
    • 2016-09-10
    • 1970-01-01
    • 2014-02-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多