Reputation: 1155
When a function returns an object by value, it will call the copy constructor to create a temporary (unless RVO is applied). That temporary will then be destroyed after use, e.g.
MyClass function_return_by_value(MyClass par)
{
return par;
}
MyClass b;
MyClass a = function_return_by_value(b); // (1)
But why do we need to create such a temporary if it is not used at all? For example, why the following code is not "optimized" by the compiler to skip the temporary creation and destroy?
MyClass b;
function_return_by_value(b); // (2)
In (1), the return value is assigned to another variable and RVO is likely to apply. But in (2), there is nothing to receive the return value, why is there no optimization take place? I have tried gcc 4.8.4 and vc++ 2015, copy constructor of MyClass is called twice for (2) in both compilers. Why do compiler makers all decide to make a temporary then destroy it even if the temporary is not used at all? Why can't they avoid this?
Upvotes: 2
Views: 281
Reputation: 238311
A function that constructs an object, and a function that does not are two different functions. When the compiler compiles a function, it does not know, how the function will be called. How would it know, which version it should compile? One that does construct or one that doesn't?
Now, let us assume that the compiler does know that within the same translation unit, the return value is never used. Conceivably, the compiler could then optimize the function to never construct the object. But what if later, in another compilation unit, that function is invoked and the result is used? The optimized version of the function would not work because it doesn't return anything!
OK, what if compiler always compiled two versions of all functions that return a value and, pick one when using the return value and other when not. Well, the compiler still wouldn't be allowed to do that, because the constructor and the destructor of the object might have side effects, and those must not simply disappear (copy elision allows that, but it's a special case and does not apply here).
OK, let's assume that the constructor and the destructor of the object are visible, when the function is compiled. Then, if the compiler can prove that there are no side effects, then it could in theory apply the optimization discussed above. Given the strict requirements for this optimization to happen (visible c'tor and d'tor with no side effects) and the adverse effects (two versions of all value returning functions would increase compilation time and possibly increase the binary size), I doubt this type of optimization has been considered seriously.
You can do this optimization manually, though. Simply separate the part of the function that constructs and returns the object, and the part of the function that has side effects:
void function_that_has_side_effects(const MyClass& par):
MyClass function_return_by_value(MyClass par)
{
function_that_has_side_effects(par);
return par;
}
Now, if you want the side effects, but don't need the return value, simply call function_that_has_side_effects
instead.
MyClass b;
function_that_has_side_effects(b); // (2)
Upvotes: 1
Reputation: 12403
The copy you are complaining about is not a copy from function return value to variable it is assigned to. It is to create the return value, as you ordered it to be a copy of function argument. Consider below code:
#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;
}
The output is
Default krowa
Copied Krowa
Default krowa
fun1
return value is a copy of parameter, so copy constructor is invoked. fun2
return value is a default constructed object, so default construcotr is invoked.
Upvotes: 1
Reputation: 385108
The side-effects of a copy can be removed by the compiler when RVO has elided it (which is fairly controversial in itself), but the entire existence of an object cannot be simply removed from your program by an optimising compiler, as long as constructing and/or destroying it has side effects.
That would allow the following program to output nothing, which is very clearly wrong:
#include <iostream>
struct A
{
A() { std::cout << "Booyah\n"; }
};
int main()
{
A a;
}
Upvotes: 4
Reputation: 36337
I have tried gcc 4.8.4 and vc++ 2015, copy constructor of MyClass is called twice for (2) in both compilers.
Because that is how it is supposed to work!
Really, for the case where you want this to be optimized, there's the reference mechanism in C++; in your case it must not be optimized away, because "I checked" implies that you relied on a side effect of the constructor to show you it's being called; how should the compiler know you don't functionally need that side effect?
Upvotes: 3