1

给定以下代码:

struct obj {
    int i;
    obj() : i(1) {}
    obj(obj &&other) : i(other.i) {}
};

void f() {
    obj o2(obj(obj(obj{})));
}

我希望发布版本只会真正创建一个对象,而不会调用移动构造函数,因为结果与执行我的代码一样。虽然大多数代码并不是那么简单,但我可以想到一些难以预测的副作用,这些副作用可能会阻止优化器证明“好像”:

  1. 在移动构造函数或析构函数中更改全局或“外部”事物。
  2. 移动构造函数或析构函数中的潜在异常(无论如何可能是糟糕的设计)
  3. 内部计数或缓存机制发生变化。

由于我不经常使用这些中的任何一个,我可以期待我的大部分进出函数,这些函数后来被内联优化掉还是我忘记了什么?

PS我知道仅仅因为可以进行优化并不意味着它将由任何给定的编译器进行。

4

2 回答 2

7

这实际上与 as-if 规则没有任何关系。允许编译器省略移动和复制,即使它们有一些副作用。这是允许编译器执行的单一优化,它可能会改变程序的结果。从§12.8/31:

当满足某些条件时,允许实现省略类对象的复制/移动构造,即使对象的复制/移动构造函数和/或析构函数具有副作用。

所以编译器不必费心检查你的移动构造函数内部发生的事情,它很可能会摆脱这里的任何移动。为了证明这一点,请考虑以下示例:

#include <iostream>

struct bad_mover
{
  static int move_count;
  bad_mover() = default;
  bad_mover(bad_mover&& other) { move_count++; }
};

int bad_mover::move_count = 0;

int main(int argc, const char* argv[])
{
  bad_mover b{bad_mover(bad_mover(bad_mover()))};
  std::cout << "Move count: " << bad_mover::move_count << std::endl;
  return 0;
}
  1. 编译g++ -std=c++0x

    Move count: 0
    
  2. 编译g++ -std=c++0x -fno-elide-constructors

    Move count: 3
    

但是,我会质疑您提供具有额外副作用的移动构造函数的任何理由。不管副作用如何都允许这种优化的想法是,复制或移动构造函数除了复制或移动之外不应该做任何事情。有复制或移动的程序应该和没有的完全一样。

尽管如此,您的电话std::move是不必要的。std::move用于将左值表达式更改为右值表达式,但创建临时对象的表达式已经是右值表达式。

于 2013-02-23T13:06:55.070 回答
2

使用std::move( tmp(...) )是完全没有意义的,临时tmp的已经是一个右值,你不需要使用std::move它来将它转换为一个右值。

阅读本系列文章:想要速度?按值传递

通过在 Stackoverflow 上提问,您将比您学到更多并理解得更好

于 2013-02-23T13:03:24.333 回答