In this answer, it's been mentioned that copy constructor is not necessarily called when passing variables by value into functions or as return values out of functions. Can someone explain when this happens and why? Also how does compiler manage to return the result in such cases?
-
1Read about [**Return Value Optimization (RVO)**](http://en.wikipedia.org/wiki/Return_value_optimization). – Nawaz Oct 18 '13 at 10:00
-
@Nawaz and how about passing by value? That is not just returning by value. – Martin Drozdik Oct 18 '13 at 10:15
-
1@MartinDrozdik if the compiler sees/knows that the value will not be modified, it may choose to pass by address – manuell Oct 18 '13 at 10:19
-
1See [**copy elision**](http://en.wikipedia.org/wiki/Copy_elision). – juanchopanza Oct 18 '13 at 10:26
-
So if the compiler sees f(M m); and only const methods of M are called in the body of f, can it silently pass M by const reference? The Wiki article does not answer this question. – Martin Drozdik Oct 18 '13 at 10:33
-
1@MartinDrozdik the compiler can do two things: 1) follow the "as-if" rule, i.e. do whatever it wants as long as the observable behaviour is not changed. Your `const` reference example may or may not violate this. 2) Elide copies, as explained in the links above. This is allowed to break the "as-if" rule. If you take an argument by value, the compiler might elide the copy just construct the object in-place. Same applies to RVO. – juanchopanza Oct 18 '13 at 10:37
1 Answers
As said, that is the Return value optimization and Copy elision.
This can happen on passing when the object is newly created and then copied. In this case, the compiler is allowed to optimize that so that the new object is directly created at the right place and no copying is needed (and also the copy constructor will not be called).
For example:
struct A {};
void test(A a) {}
int main() {
test(A()); // probably there will be no copy here
}
For returning, it is similar. You create a new object and then if you return it, that would involve a copy but the compiler is allowed to optimize that copy away (and thus also the call to the copy constructor).
For example:
A returnANewA() {
return A(); // copying would take place here
}
int main() {
A a = returnANewA(); // the compiler is allowed to do that without copying
}
How does the compiler do this: Depending on the calling convention, it knows where the return value must be stored on the stack. In other cases, it of course helps the compiler if it knows the function code. But all that depends on the architecture (x86 or others) and compiler (GCC, Microsoft, or others). The standard just says that the compiler is allowed to omit the call to the copy-constructor.
If you are interested in some platform-dependent details about calling conventions, here are some links. Note however that these details don't really matter. All you have to know is that the compiler is allowed to optimize the copy-constructor-call away (and in most cases will do so).
-
What is 'the right place'? At a low level, does a function call actually include a hidden parameter that is the address where the caller would like the result stored? If so, it's easy to understand this. But I guess there are special cases - when returning an `int` I guess the function just leaves the value on the top of the stack? – Aaron McDaid Oct 18 '13 at 11:55
-
@AaronMcDaid: No, there is no hidden parameter. But depending on the call type, it knows where the return value must be stored on the stack. In other cases, it of course helps the compiler if it knows the function code. But all that depends on the architecture and compiler. The standard just says that the compiler is allowed to omit the call to the constructor. – Albert Oct 18 '13 at 12:13
-
'it knows where the return value must be stored'. How can it just 'know'? :-) One option is that is it always stored at the top of a the stack (or at a fixed offset from the top). Alternatively, I find [this sentence](https://en.wikipedia.org/wiki/X86_calling_conventions) interesting "*To pass "in memory", the caller allocates memory and passes a pointer to it as a hidden first parameter; the callee populates the memory and returns the pointer, popping the hidden pointer when returning.*" The callee 'knows' because the caller has placed a pointer to that location as a hidden parameter. – Aaron McDaid Oct 18 '13 at 12:47
-
... in the context of that sentence, I believe "pass" means "return". It's how the callee (the function) passes the result back to the caller. – Aaron McDaid Oct 18 '13 at 12:48
-
The call convention is fixed at compilation time and the function is compiled according to the call convention. So it always puts the return value at a well-defined place (which might be defined via a hidden parameter). That is indeed interesting - I don't really know all the details about the particular x86 calling conventions. But to understand this specific optimization, it also doesn't matter that much, only that it can optimize the copy-constructor-call away. – Albert Oct 18 '13 at 13:21
-
agreed. My questions and comments don't really have any relevance to the day-to-day programming. There are occasions when the compiler can skip certain steps, in a well-defined way. I'm just being curious about how the compiler can implement these shortcuts. But my curiousity isn't helpful to the original question and answer. Thanks for your responses! – Aaron McDaid Oct 18 '13 at 13:53
-
Check out [this demo on ideone](http://ideone.com/WBl2BV). An object is created via `new` and therefore must be on the heap, not the stack. But you can see at that link that this address (a heap address) is the address where the constructor is doing its work. So the 'local variable' x2 inside func() isn't really on the stack. So even when you think you're constructing something on the stack, you may not be! – Aaron McDaid Oct 18 '13 at 13:59