views:

427

answers:

7

I have read Answers to C++ interview questions among which there is one that puzzles me:

Q: When are temporary variables created by C++ compiler?

A: Provided that function parameter is a "const reference", compiler generates temporary variable in following 2 ways.

a) The actual argument is the correct type, but it isn't Lvalue

double Cube(const double & num)
{
  num = num * num * num;
  return num;
}

double temp = 2.0;
double value = cube(3.0 + temp); // argument is a expression and not a Lvalue

b) The actual argument is of the wrong type, but of a type that can be converted to the correct type

 long temp = 3L;
 double value = cuberoot(temp); // long to double conversion

My question is once the function argument is a const reference, why does the compiler generate the temporary variable, isn't that self-contradictory? Also, should the function Cube fail to compile because it modifies the const argument?

+7  A: 

I don't see anything self-contradictory here. If the argument is not an lvalue, or is of wrong type, the reference cannot be attached directly to the argument for obvious reasons; hence the need for an intermediate temporary of the correct type. The reference is attached to that temporary instead.

The Cube function is indeed broken (ill-formed) since it attempts to modify a const value.

AndreyT
@Tracy: the truth of Andrey's answer might seem more obvious if you imagine a reference implemented as a pointer then consider `cube(const double*)` - you couldn't just call cube(4), but would have to create a temporary `double tmp = 4;` then call `cube(`. Similarly for `double a = 1, b = 2; cube(`. For your convenience, this is pretty much what the compiler can be expected to do behind the scenes :-). (True that the Standard doesn't specify how references are implemented, but it's helpful to imagine this most likely/common way.)
Tony
@Tony:thanks for your method to imagine what the compiler might do,actually,that helps me a lot to understand generating temp variables
Tracy
+3  A: 

Looks wrong to me - and gcc generates an error:

const_ref.cpp: In function ‘double cube(const double&)’:
const_ref.cpp:3: error: assignment of read-only reference ‘num’
Paul R
+2  A: 

The compiler can generate a temporary variable. It doesn't have to.

And yes, Cube should not actually compile.

MSN
Assuming the compiler doesn't inline the functions, what other option does it have than to create a temporary variable somewhere on the stack?
Oli Charlesworth
@Oli, inlining :)
MSN
A: 

Because in both examples, there is no non-temporary object of the correct type.

Oli Charlesworth
thanks,i just focus too much on the power or restrictions of passing argument by reference
Tracy
A: 

I believe that you are correct about the function cube failing to compile. Anyway, that should fail, and it does on my compiler (VC++ 2008).

As for creating a temporary:

A temporary value to back the const reference will be created whenever the actual argument:

i) is not of the correct type for the reference and, ii) can be implicitly converted to the correct type.

In example a) from your question, a temporary double is created to hold the value 3.0 + temp. Then Cube() is called with a const reference to the temporary. This is because you can't have a reference to 3.0 + temp because that isn't a variable (it is an rvalue -- the result of an expression) and so it has no memory address, and can't back the reference. Implicitly, the compiler will create a temporary double and then assign it the value of 3.0 + temp.

In your example b), you have a long, but your function requires a double. The compiler will implicitly convert a long to a double. It does this by creating a temporary double, assigning it the converted value of temp, and then creating a const reference to the temporary, and passing that reference to cuberoot

Andrew Shelansky
Gotcha,thank you very much
Tracy
A: 

Yes. Cube(), as you've shown it here, should fail to compile.

Noah Roberts
+1  A: 

You are allowed to pass the results of an expression (including that of implicit casting) to a reference-to-const. The rationale is that while (const X & value) may be cheaper to use, depending on the copy-cost of type type X, than (X value), the effect is pretty much the same; value gets used but not modified (barring some dicey const-casting). Hence it is harmless to allow a temporary object to be created and passed to the function.

You are not allowed to do so with pointer-to-const or reference-to-non-const, because unexpected (and bad) things can happen, such as you might expect the long temp to be cast back to long, which isn't going to happen.

You're correct about num = num * num * num; being invalid. That's a bug in the text, but the argument made by it holds.

Jon Hanna
@Jon:thanks for the extra limitations in pointer-to-const and reference-to-non-const,but what do you mean by "the long temp to be cast back to long".
Tracy
I mean, if it was non const then the second example you gave could be expected to change the double past to it. Since the value passed to it is not actually `temp` but a temporary double created by casting `temp`, what should be done to `temp`? There's no good answer, one possibility is casting the value back to long and assigning it to `temp`, but that's a narrowing conversion so the results could be unexpected. Another is abandoning the value, but we expect `temp` to be changed. With no good result, the language is wise to ban it.
Jon Hanna
Hi Jon:your previous explanation applied to the reference-to-non-const case,as for the pointer-to-const case,i still dont get you.Here is my question,assume the argument is pointer-to-const double,i pass temp(of long type) to that function,how come i expect temp to be changed,if i do,why i specify pointer-to-const?
Tracy
In that case there's no natural cast to use
Jon Hanna
Thanks,i guess it is because there is no way to cast a pointer-to-long to a pointer-to-double
Tracy
+1 accepted answers without upvote are just BS
ldog