不叫C ++ 11移动的构造,默认的构造优选(C++11 move constructor not

2019-06-18 16:16发布

假设我们有这个类:

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_;

};

这个示例用法:

X x("test");
cout << x.c() << endl;
X y(x);
cout << y.c() << endl;
X z( X("test") );
cout << z.c() << endl;

输出是:

ctor
test
copy
test
ctor   <-- why not move?
test

我使用VS2010采用默认设置。 我期望的最后目标( z )被移动构建的,但它不是! 如果我用X z( move(X("test")) ); 那么输出的最后一行是ctor move test ,如我期望。 它是(N)RVO的情况?

:如果布展构造函数根据标准被称为? 如果是这样,为什么不叫?

Answer 1:

你看到的是复制省略 ,这允许编译器直接构造一个临时到目标是要被复制/移动到并且因此一个的Elid副本(或移动)构造/析构对。 其中,编译器允许应用复制省略在C ++ 11标准的§12.8.32指定的情况:

当满足特定条件时,一种实现被允许省略类对象的复制/移动结构,即使在复制/移动的构造和/或析构函数为对象具有侧e FF学分。 在这种情况下,执行对待省略复制/移动操作的源和目标作为根本就是两个指相同的对象的二FF erent方式,和该对象的破坏在时间的后期发生在当两个物体会一直不破坏优化。 复制/移动操作的这个省音,叫做复制省略,在下列情况下是允许的(这可能合并,以消除多个副本):

  • 在与类返回类型的函数返回语句,当表达是一种非挥发性的自动物体的名称
    相同的CV-unquali音响ED型作为函数返回类型,所述
    复制/移动操作可以通过构建自动被省略
    对象直接插入函数的返回值
  • 在掷表达,当操作数是一个非易失性自动对象,其范围不延伸超出最里面的封闭的try块的端部的名称(如果有的话),从操作数到复制/移动操作异常对象(15.1)可以通过直接构建自动对象到异常对象可以省略
  • 当尚未结合至参考(12.2)将被复制临时类对象/移动到一个类对象与他同一CV-unquali音响版型中,复制/移动操作可以通过直接构建临时对象到目标被省略的
    省略复制/移动
  • 当一个异常处理程序(第15)的异常声明声明同一类型的对象(除了CV-合格音响阳离子),为
    异常对象(15.1),可以省略复制/移动操作
    bytreatingthe异常声明作为例外的别名
    Object如果程序的意义将是除了构造函数和析构函数的执行通过声明的对象不变
    在异常声明。


Answer 2:

ctor输出你在第三行代码得到的是对临时对象的构造。 在此之后,的确,临时移动到新的变量z 。 在这种情况下,编译器可以选择在的Elid复制/移动,似乎这是它的所作所为。

该标准规定:

(§12.8/ 31)当满足特定条件时,一种实现被允许省略类对象的复制/移动结构,即使在复制/移动的构造和/或析构函数为对象具有侧e FF学分。 [...]复制/移动操作的此省音,称为复制省略,允许在下列情况下(其可以被组合以消除多个副本):
[...]
- 当尚未结合至参考(12.2)将被复制临时类对象/移动到一个类对象具有相同的CV-unquali音响版型,复制/移动操作可以通过直接构建临时物体插入省略省略的复制/移动的目标
[...]

一个重要的条件是源对象和目的地是相同的类型(除了CV-资格,像即东西的const )。

因此,您可以强制转移构造函数被调用一个方法是将对象的初始化与隐式类型转换相结合

#include <iostream>

struct B
{};

struct A
{
  A() {}
  A(A&& a) {
    std::cout << "move" << std::endl;
  }
  A(B&& b) {
    std::cout << "move from B" << std::endl;
  }
};


int main()
{
  A a1 = A(); // move elided
  A a2 = B(); // move not elided because of type conversion
  return 0;
}


Answer 3:

要调用X's char*构造X("test")明确。

因此,这是印刷ctor



文章来源: C++11 move constructor not called, default constructor preferred