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

vlurs2pr  于 2023-07-01  发布在  其他
关注(0)|答案(4)|浏览(144)

假设我们有这个类:

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病例?

Q:是否应该按照标准调用move-actor?如果是这样,为什么不叫它?

llew8vvj

llew8vvj1#

你看到的是copy elision,它允许编译器直接将一个临时对象构造成它要复制/移动到的目标,从而省略了复制(或移动)构造函数/析构函数对。允许编译器应用复制省略的情况在C++11标准的§12.8.32中指定:
当满足某些条件时,允许实现省略类对象的复制/移动构造,即使对象的复制/移动构造函数和/或析构函数具有副作用。在这种情况下,实现将省略的复制/移动操作的源和目标简单地视为引用同一对象的两种不同方式,并且该对象的销毁发生在两个对象在没有优化的情况下被销毁的时间的较晚时间。这种复制/移动操作的省略(称为复制省略)在以下情况下是允许的(可以组合以消除多个副本):

  • 在具有类返回类型的函数的return语句中,当表达式是具有

与函数返回类型相同的艾德类型,
复制/移动操作可以通过构造自动
对象直接转换为函数的返回值

  • 在throw-expression中,当操作数是一个非易失性自动对象的名字,它的作用域没有超出最里面的封闭try块(如果有的话)的末尾,从操作数到异常对象的复制/移动操作(15.1)可以通过将自动对象直接构造到异常对象中来省略
  • 当一个没有绑定到引用(12.2)的临时类对象将被复制/移动到具有相同cv-未限定类型的类对象时,复制/移动操作可以通过将临时对象直接构造到

省略复制/移动

  • 当异常处理程序的异常声明(第15章)声明了与

异常对象(15.1),则可以省略复制/移动操作
通过将异常声明视为异常的别名
声明的对象的构造函数和析构函数执行之外,程序的含义将保持不变
异常声明。

8gsdolmq

8gsdolmq2#

在第三个代码行中得到的ctor输出用于构造临时对象。在此之后,实际上,临时变量被移动到新变量z中。在这种情况下,编译器可能会选择省略复制/移动,看起来它就是这么做的。
该标准规定:
(§12.8/31)当满足某些条件时,允许实现省略类对象的复制/移动构造,即使对象的复制/移动构造函数和/或析构函数有副作用。[...]这种复制/移动操作的省略,称为复制省略,在以下情况下是允许的(可以组合以消除多个副本):
[...]

  • 当一个没有绑定到引用(12.2)的临时类对象将被复制/移动到具有相同cv-未限定类型的类对象时,可以通过将临时对象直接构造到被省略的复制/移动的目标中来省略复制/移动操作
    [...]
    一个重要的条件是源对象和目标对象的类型相同(除了cv限定,即const)。
    因此,可以强制调用move构造函数的一种方法是将对象初始化与 * 隐式类型转换结合起来:*
#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;
}
8tntrjer

8tntrjer3#

您正在显式调用X'schar*构造函数X("test")
因此打印ctor

t0ybt7op

t0ybt7op4#

只是想评论一下,如果你只想确保移动ctor工作,你可以通过抛出一个条件来破解代码以消除编译器优化,例如:

X z( some_val > 1 ? X("test") : X("other test"));

相关问题