给出以下代码:
struct obj {
int i;
obj() : i(1) {}
obj(obj &&other) : i(other.i) {}
};
void f() {
obj o2(obj(obj(obj{})));
}
我希望发布版本只能真正创建一个对象,而不会调用移动构造函数,因为结果与执行代码的相似。大多数代码并不是那么简单,我可以想到一些难以预测的副作用可能会阻止优化器证明“好像”:
由于我不经常使用这些中的任何一种,我可以期待我的大部分进出后期内联功能的动作都被优化掉了或者我忘记了什么?
P.S。我知道只是因为优化是可能的并不意味着它将由任何给定的编译器完成。
答案 0 :(得分: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;
}
使用g++ -std=c++0x
编译:
Move count: 0
使用g++ -std=c++0x -fno-elide-constructors
编译:
Move count: 3
但是,我会质疑你提供具有额外副作用的移动构造函数的任何原因。允许此优化而不考虑副作用的想法是复制或移动构造函数不应该执行除复制或移动之外的任何操作。复制或移动的程序应与没有复制或移动的程序完全相同。
尽管如此,您对std::move
的来电是不必要的。 std::move
用于将左值表达式更改为右值表达式,但创建临时对象的表达式已经是右值表达式。
答案 1 :(得分:2)
使用std::move( tmp(...) )
完全毫无意义,临时tmp
已经是右值,您无需使用std::move
将其转换为右值。
阅读本系列文章:Want Speed? Pass By Value
通过在Stackoverflow上提出问题,您将学到更多,并且比您更了解