当函数按值返回对象时,它将调用复制构造函数来创建临时(除非应用了RVO)。然后该临时用户将在使用后销毁,例如
MyClass function_return_by_value(MyClass par)
{
return par;
}
MyClass b;
MyClass a = function_return_by_value(b); // (1)
但是,如果根本不使用,为什么我们需要创建这样一个临时的?例如,为什么以下代码不被优化"由编译器跳过临时创建并销毁?
MyClass b;
function_return_by_value(b); // (2)
在(1)中,返回值被分配给另一个变量,并且可能适用RVO。但是在(2)中,没有什么可以获得返回值,为什么没有优化发生?我已经尝试过gcc 4.8.4和vc ++ 2015,在两个编译器中,MyClass的复制构造函数被调用两次(2)。为什么编译器制造商都决定暂时销毁它,即使暂时不使用它?为什么他们不能避免这种情况?
答案 0 :(得分:4)
当RVO将其删除时,编译器可以删除副本的副作用(这本身就很有争议),但是对象的整个存在不能简单地通过优化编译器从程序中删除,如构建和/或销毁它会产生副作用。
这将允许以下程序输出任何内容,这显然是错误的:
#include <iostream>
struct A
{
A() { std::cout << "Booyah\n"; }
};
int main()
{
A a;
}
答案 1 :(得分:3)
我尝试过gcc 4.8.4和vc ++ 2015,在两个编译器中,(2)两次调用MyClass的复制构造函数。
因为它应该如何运作!
真的,对于你想要优化它的情况,C ++中有参考机制;在你的情况下,不能被优化掉,因为“我检查”意味着你依靠构造函数的副作用来向你显示它被调用;如何编译器知道你在功能上不需要那种副作用?
答案 2 :(得分:1)
您抱怨的副本不是从函数返回值到分配给它的变量的副本。它是创建返回值,因为您将它命令为函数参数的副本。请考虑以下代码:
#include <iostream>
class Krowa
{
public:
Krowa() {std::cout << "Default krowa\n";}
Krowa(Krowa const &) {std::cout << "Copied Krowa\n";}
};
Krowa fun1(Krowa const & krowa)
{
return krowa;
}
Krowa fun2()
{
return Krowa{};
}
int main()
{
fun1(Krowa{});
fun2();
return 0;
}
输出
默认krowa
复制的Krowa
默认krowa
fun1
返回值是参数的副本,因此调用了复制构造函数。 fun2
返回值是默认的构造对象,因此调用默认的construcotr。
答案 3 :(得分:1)
构造对象的函数,以及不是两个不同函数的函数。当编译器编译一个函数时,它不知道如何调用该函数。怎么知道它应该编译哪个版本?一个构建的或一个不构建的
现在,让我们假设编译器确实知道在同一个翻译单元中,从不使用返回值。可以想象,编译器然后可以优化函数以永远不构造对象。但是,如果稍后在另一个编译单元中调用该函数并使用结果 ,该怎么办?该功能的优化版本不起作用,因为它不会返回任何内容!
好吧,如果编译器总是编译返回值的所有函数的两个版本,并且在使用返回值时选择一个,而在不使用时选择其他函数。好吧,编译器仍然不允许这样做,因为对象的构造函数和析构函数可能有副作用,并且那些不能简单地消失(复制省略允许这样做,但它是特殊情况)并且不适用于此。)
好的,我们假设在编译函数时,对象的构造函数和析构函数是可见的。然后,如果编译器可以证明没有副作用,那么它理论上可以应用上面讨论的优化。鉴于对此优化的严格要求(可见c&tor; d没有副作用)和不利影响(所有值返回函数的两个版本会增加编译时间并可能增加二进制大小) ,我怀疑这种类型的优化是否被认真考虑过。
但是,您可以手动执行此优化。简单地分离构造和返回对象的函数部分,以及具有副作用的函数部分:
void function_that_has_side_effects(const MyClass& par):
MyClass function_return_by_value(MyClass par)
{
function_that_has_side_effects(par);
return par;
}
现在,如果您想要副作用,但不需要返回值,只需拨打function_that_has_side_effects
即可。
MyClass b;
function_that_has_side_effects(b); // (2)