假设我们有一个功能:
Foo bar(Foo&& foo)
{
// assume `Foo` is move constructible
return std::move(foo);
}
在这个例子中:
// (1)
Foo foo = bar(Foo{});
这一个:
// (2)
Foo foo;
// do something with `foo` so that compiler can't optimize it away
...
Foo foo2 = bar(std::move(foo));
我知道在(2)中我们几乎肯定无法避免默认构造(foo
)和移动构造(返回值bar
),但第三步移动构造(从返回编译器可以省略bar
到foo2
)的值。
但是(1)怎么样?在语义上有两个移动构造(一个从临时参数到bar
的返回值,另一个从返回值到foo
),其中第二个几乎总是被省略。但是第一个怎么样?是否有可能一些积极的优化只能使示例(1)中的一个构造发生,因此它实际上与以下内容相同:
Foo foo;
这可能吗?
答案 0 :(得分:0)
可以,但在此意图之外使用并不完全安全,请注意使用此功能。
您可以使用rvalues完全忽略构造,但这意味着您的中间函数(即bar
)必须传递并接受参考参数。仅仅考虑右值引用并不难,所以这样的事情是可能的:
class A { /* ... */ };
A && bar (A && a) {return std::move(a);}
A a1 = A();
A a2 = bar( A() );
A a3 = bar( bar( A() ) );
经验法则:如果您按值传递,则只能通过优化删除虚假中间对象实例,因为编译器必须检查实例的使用情况并重新排列。但是对于引用,我们直接声明我们需要一个已经可用的对象,并且rvalues将它扩展为临时对象。
"陷阱"如果您传递对范围已过期的对象的引用,则您将遇到麻烦。 (例如,将rvalue引用返回到局部变量,不比常规引用更好,但是这种情况很可能被编译器标记,而rvalues可能不会)。
A && bad_valid_return() {
A temp;
return std::move(temp);
}
A a4 = bad_valid_return(); // not ok, object is destroyed once we return!
A good_valid_return() {
A temp;
return std::move(temp);
// better but we can only remove the result's construction via optimize
}
为了它的价值你也可以这样做:
A && a5 = good_valid_return();
a5
本质上是一个常规变量(它的类型为A&
,当使用时几乎与A a5
相同。它将保存返回的实际临时,因此临时构造就位。这甚至可以绕过私有对象成员 - a5
即使operator=
和构造函数是私有的(good_valid_return
),任务也会有效。
bar
在一般情况下,几乎绝不允许完全避免移动操作符。考虑每个"按值"作为构造障碍传递的值 - 越过该点的值必须产生一个新的容器,除了,其中"显而易见的"只需要一个物体。
这些"显而易见的"案例是返回值优化案例。作为单独的代码单元,函数无法知道它们的值将如何被使用,因此函数以"值进行交易"传递必须假定需要一个新的对象。
这个例外就是内联 - 函数代码在被引用的地方进行了优化,基本上完全删除了函数调用 - 我希望这是你的(1)案例唯一一次优化远离任何额外的对象/移动/复制而不使用右值引用直接告诉编译器你打算发生什么。
它看起来像可以在(1)中发生的那样,但是你需要至少{GAC}优化GCC,例如,为此。 (也许是O2,但我不会这么认为)
答案 1 :(得分:0)
如果我没记错的话,RVO并不适用于此,原因有两个:
但是,如果您的复制/移动构造函数没有任何副作用(不能与deepmax的Foo类一起使用)并且条形很简单,我很确定现代编译器能够内联条形并优化掉除了as-if规则下的default-constructor之外的所有内容。