正如这个wiki页面所说的(代码如下所示),返回值优化是C++编译器允许的,但仍然取决于实现。为了减少复制的成本,建议手动优化它(将函数的对象赋给引用,如const C& obj = f();),还是让编译器在实践中进行这种优化?
#include <iostream>
struct C {
C() {}
C(const C&) { std::cout << "A copy was made.\n"; }
};
C f() {
return C();
}
int main() {
std::cout << "Hello World!\n";
C obj = f();
}编辑:将更改更新为常量引用。
发布于 2014-11-25 19:30:25
您不能(可移植地)使用临时返回值来初始化非常数引用,因此肯定不推荐这样做。
使用它初始化const引用对返回表达式的值的复制/移动是否可以省略没有任何影响;尽管它会从返回值中消除用于初始化变量的名义上的复制/移动,无论是否已经省略。当然,这与初始化(非引用)变量不同,因为您不能修改它。
实际上,任何具有良好优化器的编译器都会省略复制,并在允许的情况下移动到任何地方。如果你没有使用一个像样的优化器,那么无论如何你也不能期望有像样的性能。
发布于 2014-11-25 22:03:16
要手动确保不会获得对象的任何冗余副本,您需要做的工作比到目前为止所做的要多一点。之前我回答说这是不可能的,但我错了。此外,使用const&可能不允许对返回值执行某些您希望允许的操作。如果你需要手动进行优化,我会这么做:
#include <iostream>
struct S {
S()
{ std::cout << "default constructor\n"; }
S(const S &)
{ std::cout << "copy constructor\n"; }
S(S &&)
{ std::cout << "move constructor\n"; }
~S()
{ std::cout << "destructor\n"; }
};
S f() { return {}; }
int main() {
auto&&s = f();
std::cout << "main\n";
}这将打印"default destructor“,然后是"main",然后是"destructor”。无论是否发生任何复制省略,这都是输出。在main中,s是一个命名引用,所以它是一个左值,而不是const-qualified。你可以用它做任何你本来可以做的事情。
鉴于事实证明,在这样的情况下避免依赖复制省略相当容易,只要您从一开始就注意到它,如果您不得不担心其他编译器不执行复制省略,那么您的努力可能是值得的。大多数编译器都能做到这一点,如果编译器不能做到这一点,它很有可能会遇到其他更大的问题,所以有一个很好的理由不用担心这个问题。
然而,同时,复制省略在某种程度上是不可靠的:即使是当前的优化编译器也不总是执行它,原因很简单,在某些情况下,复制省略是有意义的,但标准不允许,或者对于特定的实现是不可能的。强迫自己编写不依赖于复制省略的代码意味着您不能陷入这种情况。
也就是说,在某些情况下,复制遗漏只能通过优化编译器来实际消除,因此您可能别无选择,只能依赖它:
假设我们将void m();添加到S的定义中。假设我们现在编辑f以
S f() {
S s;
s.m();
return s;
}这就更难重写成一种保证没有冗余副本的形式。然而,同时,副本是不必要的,这可以很容易地从以下事实中确定:使用GCC (可能还有其他编译器),默认情况下,不会创建副本。
我最后的结论是,对于不执行RVO的编译器来说,它可能不值得优化,但值得仔细考虑到底是什么让它工作,并以这样一种方式编写代码,即RVO不仅是可能的,而且成为编译器很可能做的事情。
https://stackoverflow.com/questions/27125304
复制相似问题