4

为什么有时不移动构造函数调用?测试移动语义(实时代码)

struct Test {
    int id;
    Test(int id) : id(id) {
        cout << id << "  Test() " << endl;
    }
    ~Test() {
        cout << id << "  ~Test() " << endl;
    }
    Test(const Test &t) : id(t.id) {
        cout << id << "  Test(const Test &t) " << endl;
    }
    Test(Test &&t) : id(t.id) {
        cout << id << "  Test(Test &&t) " << endl;
    }
    Test &operator=(const Test &t) {
        cout << id << "  operator=(const Test &t) " << endl;
        return *this;
    }
    Test &operator=(Test &&t) {
        cout << id << "  operator=(Test &&t) " << endl;
        return *this;
    }
};

void f(Test z) {
    cout << z.id << "  f(Test z) " << endl;
}

int main() {
    f(Test(1));

    Test t(2); f(t);
}

输出:

1  Test() 
1  f(Test t)               <---// where is move constructor ?!
1  ~Test() 
2  Test() 
2  Test(const Test &t)     <---// copy constructor of t(2)
2  f(Test t) 
2  ~Test() 
2  ~Test()

测试显示复制构造函数被调用。

但是,在没有调用右值对象的移动构造f(Test(1));函数的情况下调用函数之后。fTest(1)

它是隐式编译器优化吗?还是我错过了重要的一点?

4

1 回答 1

8

编译器被明确允许删除临时对象的副本(或移动)。基本上,对象是在预期有效结果的地方构建的。如果构造函数或析构函数有副作用,甚至允许这种省略。

相关条款是 12.8 [class.copy] 第 31 段:

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

可以使用复制省略的情况基本有

  1. return返回临时变量或局部变量时的In语句。
  2. throw抛出临时变量或局部变量时的表达式中。
  3. 何时复制临时对象。
  4. 按值捕获对象时。

可以省略副本的确切条件在 12.8 [class.copy] 第 31 段中列出。

防止复制/移动省略的最简单方法是将其传递给返回合适引用的函数,例如,使用

f(std::move(Test(1)));

应该防止移动省略。

于 2013-10-03T21:21:50.710 回答