C++ 11移动构造函数未调用,默认构造函数首选

eme*_*esx 21 c++ move-semantics c++11

假设我们有这个类:

class X {
public:
    explicit X (char* c) { cout<<"ctor"<<endl; init(c); };
    X (X& lv)  { cout<<"copy"<<endl;  init(lv.c_); };
    X (X&& rv) { cout<<"move"<<endl;  c_ = rv.c_; rv.c_ = nullptr; };

    const char* c() { return c_; };

private:
    void init(char *c) { c_ = new char[strlen(c)+1]; strcpy(c_, c); };
    char* c_;

};
Run Code Online (Sandbox Code Playgroud)

以及此示例用法:

X x("test");
cout << x.c() << endl;
X y(x);
cout << y.c() << endl;
X z( X("test") );
cout << z.c() << endl;
Run Code Online (Sandbox Code Playgroud)

输出是:

ctor
test
copy
test
ctor   <-- why not move?
test
Run Code Online (Sandbox Code Playgroud)

我使用VS2010默认设置.我希望最后一个object(z)是移动构造的,但事实并非如此!如果我使用X z( move(X("test")) );那么输出的最后几行是ctor move test,正如我所期望的那样.是(N)RVO的情况吗?

:是否应该按照标准调用移动器?如果是这样,为什么不称它?

Gri*_*zly 24

你所看到的是copy elision,它允许编译器直接构造一个临时的目标,它将被复制/移入并因此忽略一个复制(或移动)构造函数/析构函数对.允许编译器应用复制省略的情况在C++ 11标准的第12.8.32节中规定:

当满足某些条件时,允许实现省略类对象的复制/移动构造,即使对象的复制/移动构造函数和/或析构函数具有副作用.在这种情况下,实现将省略的复制/移动操作的源和目标简单地视为引用同一对象的两种不同方式,并且该对象的破坏发生在两个对象的后期时间.没有优化就被破坏了.在下列情况下(允许组合以消除多个副本),允许复制/移动操作(称为复制省略)的省略:

  • 在具有类返回类型的函数的return语句中,当表达式是具有与
    函数返回类型相同的cv-unquali fi ed类型的非易失性自动对象的名称时,
    可以通过构造复制/移动操作来省略复制/移动操作自动
    对象直接进入函数的返回值
  • 在throw-expression中,当操作数是非易失性自动对象的名称时,其范围不会超出最内层封闭的try-block(如果有的话)的末尾,从操作数到操作数的复制/移动操作通过将自动对象直接构造到异常对象中,可以省略异常对象(15.1)
  • 当一个未绑定到引用(12.2)的临时类对象被复制/移动到具有相同cv-unquali fi ed类型的类对象时,可以通过将临时对象直接构造到目标中来省略复制/移动操作的
    省略复制/移动
  • 当异常处理程序的异常声明(第15节)声明一个相同类型的对象(cv-quali fi cation
    除外)作为异常对象(15.1)时,可以通过将
    异常声明作为别名来删除复制/移动操作对于异常
    对象,如果程序的含义将保持不变,除了执行
    异常声明声明的对象的构造函数和析构函数.

  • @elmes:`X z((rand()%2)?X("test"):X("TEST"));` (2认同)
  • @Xeo:我不跟进.即使在所描述的情况下,标准也可能允许复制省略,但是编译器是否可以这样做,特别是如果寿命重叠,则是有问题的.我在哪里说在返回局部变量时需要(或者是可取的)显式移动? (2认同)