built-in type variable is returned from a function
If a local object is returned in a function call, it has to do at least three steps:
- Copy constructor is called to hold a copy.
- Destroy local object.
- A copy is ret开发者_如何学编程urn.
For example:
x = y + z
If x
is an integer object. A copy of y + z
should be returned, then a new object is created, then assignment operator of x
will take this object as parameter.
So my questions are:
- Is the same process used for built-in type such as
int
,double
...? - If they're not the same, how's it done?
The language specification does not say "how it is done" for built-in types and built-in operators. The language simply says that the result of binary +
for built-in types is an rvalue - the sum of the operand values. That's it. There's no step-by-step description of what happens when a built-in operator is used (with some exceptions like &&
, ,
etc.).
The reason you can come up with a step-by-step description of how an overloaded operator works (which is what you have in your question) is because the process of evaluating overloaded operator comes through several sequence points. A sequence point in a C++ program implements the concept of discrete time: it is the only thing that separates something that happens before from things that happen after. Without a separating sequence point, there's no "before" and no "after".
In case of an overloaded operator, there quite a few sequence points involved in the process of its evaluation, which is why you can describe this process as a sequence of steps. The process of evaluation of built-in operator +
has no sequence points in it, so there absolutely no way to describe what happens there in step-by-step fashion. From the language point of view, the built-in +
is evaluated through a blurry indivisible mix of unspecified actions that produce the correct result.
It is done this way to give the compiler better optimization opportunities when evaluating built-in operators.
This depends on several factors, especially the compiler's level of or capacity for optimization. This can also, to some extend, depend on the calling convention.
All built-in types can fit on a register (except for "unusually large" built-in types like "long long int"). Basically, for all calling conventions, if the return type can fit on the EAX register, that is where it's put by the callee and retrieved by the caller. So, that would be the answer to your question.
For larger objects, the procedure that you described is only true in principle, but this whole copy-destroy-temporary-copy-destroy thing is very inefficient and it is amongst the highest priorities of any compiler's optimization algorithm. Since objects are too large to fit on a register. They are typically put on the stack and left there to be retrieved by the caller. Since very often, they are just stored right back into another local variable, compilers will try to merge those stack slots together, and also, often the local variable in the called function will also be at the same slot, so, at the end, you get no copy, no destruction, no temporary, no overhead... that's the ideal "optimized" situation, but the compiler is not always able to realize that and it also requires the object to be of a POD class.
If you're just talking about the example you have right now (x = y + z
), then there is no function call - the addition takes place right in the registers.
If you're actually calling a function (x = sum(y, z)
, say), then you can get a couple different behaviours based on calling conventions and data types (types that can't fit in a single register get special treatment), but with int
s, it's pretty safe to assume they'll end up passed back in the EAX register.
No constructors / destructors are involved!
For more about the individual data types, check out the Return Values section on this page - I think these are for the cdecl calling convention, but they should be pretty ubiquitous.
For more on calling conventions in general, Wikipedia (x86 calling conventions) does a pretty thorough job. You'll be interested in cdecl (standard C functions) and thiscall (C++ class-member functions) in particular.
Hope this helps!
There are two questions here. First, your list about what needs to be done for returning a user-defined type from a function is basically correct; except that all actual compilers use return value optimization to avoid the temporary copy.
The other question is "what about built-in types?" Conceptually the same thing happens, it's just that (1) built in types have "trivial constructors" and "trivial destructors" (i.e., the compiler knows there's no need to actually call any functions to construct/destruct these types), (2) the compiler knows more about operations on the built-in types than operations on user-defined types and won't actually need to call functions to, say, add two int
s (instead the compiler will just use the relevant assembly code instructions, and (3) the compiler knows more about built-in types than user-defined types and can use return value optimization even more often.
For the record, rvalue references and related changes to C++-0x are largely about giving the programmer more ability to control things like return value optimization.
精彩评论