C++ 11移动构造函数优化

Áko*_*dra 2 c++ move-semantics c++11

我目前正试图抓住移动构造函数.我发现了以下内容(编译使用g++ d.cpp --std=c++11 -O3)

class A {
    string _x;
public:
  A(string x) { cout << "default contrsutctor: " << x << "\n"; _x = x; }
  A(const A& other) { cout << "copy contrsutctor: " << other._x << "\n"; _x = other._x;  }
  A(A&& other) { cout << "move contrsutctor: " << other._x << "\n"; _x = other._x;  }

  A foo() {
    cout << "foo: " << _x << "\n";
    return A("foo");
  }
};

int main()
{
  A a;
  A b = a;
  b.foo();
}
Run Code Online (Sandbox Code Playgroud)

我希望这输出:

default contrsutctor: a
move contrsutctor: a
foo: a
default contrsutctor: foo
Run Code Online (Sandbox Code Playgroud)

但输出是:

default contrsutctor: a
copy contrsutctor: a
foo: a
default contrsutctor: foo
Run Code Online (Sandbox Code Playgroud)

为什么不A b = a优化使用移动构造函数的行?之后永远不会使用a对象,因此优化代码以使用它而不是复制构造函数是安全的.

我知道我可以强制调用移动构造函数std::move(),但我更喜欢在这种情况下自动发生这种情况.

Dan*_*ang 7

为什么不使用移动构造函数优化A b =一条线?

您可以在复制构造函数和移动构造函数中执行的操作可能完全不同.编译器无法保证两个构造函数的结果相同.实现这种优化有可能改变程序的行为,从而破坏了as-if规则.

您需要使用std::move转换aA&&:

#include <utility>
int main()
{
  A a("a");
  A b = std::move(a);
  b.foo();
}
Run Code Online (Sandbox Code Playgroud)

移动构造函数的正确实现应该是:

A(A&& other)
: _x(std::move(other._x))
{}
Run Code Online (Sandbox Code Playgroud)

行后A b = std::move(a);,a应该是"空".在这种情况下,a._x将是空的.正如@TonyD在评论中指出的那样,a._str可能处于未指定但有效的状态(移动std:string的构造函数).你应该a在这一行之后谨慎使用.

  • 另一个正确的实现:`A(A &&)=默认;`:) (2认同)
  • "在行'A b = std :: move(a)之后; a`应该是"空".在这种情况下,`a._x`将为空." - 这真的是在任何地方强制执行吗?cppreference说"其他人留在有效但未指明的状态." 我的理解是字符串移动构造函数可以自由地"窃取"任何指针,我想要它,但如果字符串适合短字符串优化内部缓冲区,我不希望或期望移动构造函数花时间清除'a._x`:你显然不在乎那里留下了什么. (2认同)

Dan*_*vil 5

A b = a;总是调用复制构造函数,无论它是否可以调用移动构造函数.此外,对象的生命周期a在分配后继续,即使它不再使用.

如果要使用移动构造函数,则必须将其显式化:

A b = std::move(a);
Run Code Online (Sandbox Code Playgroud)

请注意,这可能很危险,因为a移动后仍然可以访问.如果您以后意外使用它,可能会有未定义的行为.

想想为什么它应该自动发生.在您给出的示例中,没有必要,因为您可以使用a而不是b.在许多情况下,它会更有意义移动构造函数/赋值将被自动使用,例如A a; a = foo();.