【问题标题】:Does std::move work with lvalue references? How does std::move work on standard containers?std::move 是否适用于左值引用? std::move 如何在标准容器上工作?
【发布时间】:2016-12-19 10:02:51
【问题描述】:
#include <vector>

struct A { int a[100]; };

void foo (const A& a) {
  std::vector<A> vA; 
  vA.push_back(std::move(a));  // how does move really happen?
}

int main () {
  A a;
  foo(a);
}

上面的代码编译得很好。现在到处都写着move 避免复制。
以下是我的疑问:

  1. move 在处理左值时真的有效吗 [非]-const 参考?
  2. 即使使用“右值引用”,当对象时如何避免复制 插入到上面的标准容器中?

例如

void foo (A&& a) {  // suppose we invoke this version
  std::vector<A> vA; 
  vA.push_back(std::move(a));  // how copy is avoided?
}

【问题讨论】:

  • 它在std::move(a) 产生const A &amp;&amp; 的意义上起作用(请记住std::move 只是将其参数转换为右值引用)。但是没有push_back(const A &amp;&amp;) 这样的东西——只有push_back(const A &amp;)push_back(A &amp;&amp;)--,第一个将被调用,导致复制发生(因为你不能将 const rvalue ref 绑定到 non -const rvalue ref,但您可以将其绑定到 const 左值引用)。
  • 你是什么意思,“如何避免复制?”。你问vector::push_back(T &amp;&amp;)是怎么实现的?
  • @SteveJessop,在某种程度上是的。但我最好不要详细说明。我的基本问题是,因为来自外部的A&amp;&amp; a 可能会在某个位置创建。 std::vector 是连续容器。怎么可能完全避免复制?
  • @iammilind: 好吧,如果值类型 T 是代码中的 struct A,移动该类型与复制它完全相同,所以复制 isn '不避免。对于具有有用移动构造函数的类型(例如 vector 本身具有),它比复制构造函数的执行速度更快,那么我们就可以开展业务了。但是,如果一个类型将其所有数据存储在对象本身内部(即没有像 vector 这样的外部数据结构),那么一般来说,您无法编写有用的移动构造函数/赋值。
  • 要记住的是Copyable这个概念是Movable概念的一个子集。可自动复制的所有东西也是可移动的,即使C++ 所指的移动实际上只是作为副本实现的。这就是为什么移动没有指定源处于什么状态的部分原因:允许它根本不改变它的状态。然而并非所有可移动的东西也是可复制的(例如unique_ptr),并且对于某些可复制的东西,移动与复制不同(例如vector)。

标签: c++ c++11 reference language-lawyer move


【解决方案1】:

std::move 不动。它实际上将左值引用转换为右值引用。在这种情况下,移动的结果是const A &amp;&amp;(顺便说一句,这完全没用)。

std::vector 具有 const A &amp;A &amp;&amp; 的重载,因此 const A &amp; 的重载将被选中,const A &amp;&amp; 被隐式转换为 const A &amp;

std::move 可以在 const 对象上调用,这对大多数程序员来说是奇怪/意外的行为,尽管它在某种程度上是允许的。 (很可能他们有一个用例,或者没有阻止它)

对于您的示例,更具体地说,将调用 A 类的移动构造函数。由于 A 是一个 POD,这很可能只是进行复制,因为所有位只需移动/复制到 A 的新实例。

由于标准仅规定原始对象必须处于有效但未指定的状态,您的编译器可以将 A 中的位保留在适当的位置,而不必将它们全部重置为 0。实际上,大多数编译器会保留这些位就位,因为更改它们需要额外的指令,这对性能不利。

【讨论】:

  • 为什么奇怪? const T &amp; 可以绑定到右值这一事实在 C++11 中并不新鲜。
  • 当你在 C++ 中考虑到这一点时,它就不再奇怪了,将std::move 添加到一些否则会导致复制的代码中,一般来说,可移动概念的意思是“如果可能的话移动,否则复制” .它们并不意味着“如果可能移​​动,否则无法编译”。
  • 好吧,那么,为什么奇怪呢? const-qualified rvalues 在 C++11 中也不是新的。 :)
  • 不避免复制。您的两个 foo 函数的行为将完全相同,因为您尚未定义移动构造函数(您的 A 类也没有任何可移动资源)。
  • @MilesBudnek Move 构造函数在这种情况下是隐式定义的,虽然最后你必须复制A::a,因为数组是不可移动的,但第一个版本的foo 将调用 push_back(A const&amp;) 而第二个将调用 push_back(A&amp;&amp;),因此这些函数的行为并不完全相同。
【解决方案2】:

创建了一个 sn-p 来显示它。虽然在您的示例中将调用默认构造函数,但您明白了。

#include <vector>
#include <iostream>

struct A { 
  int a[100];
  A() {}
  A(const A& other) {
    std::cout << "copy" << std::endl;
  }
  A(A&& other) {
    std::cout << "move" << std::endl;
  }
};

void foo(const A& a) {
  std::vector<A> vA; 
  vA.push_back(std::move(a));
}

void bar(A&& a) {
  std::vector<A> vA; 
  vA.push_back(std::move(a));
}

int main () {
  A a;
  foo(a);            // "copy"
  bar(std::move(a)); // "move"
}

【讨论】:

  • foo(std::move(a));更能说明问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-17
  • 1970-01-01
  • 1970-01-01
  • 2015-03-28
  • 1970-01-01
  • 2017-11-18
  • 1970-01-01
相关资源
最近更新 更多