tags:

views:

506

answers:

11

There was some code like this:

// Convenience to make things more legible in the following code
const float & x = some.buried.variable.elsewhere;

// Go on to use x in calculations...

I have been told that the "const float &" is "bad" and should just be a plain float or const float.

I, however, could not think of a compelling reason other than "you don't have to type '&'".

In fact, it seems to me that in some cases the original could be better, since compiler might not allocate extra stack space to the variable.

In other words, originally I could validly say:

assert(&x == &some.buried.variable.elsewhere)

Whereas in the second case I cannot.

Also, the original seems to communicate intent better, in my view, since the whole point of a reference is to make an alias to another value.

Can anyone give me examples of where the "const float &" version is worse than a plain "float" or "const float" in some tangible way?

A: 

Whenever you are dealing with a reference you are taking a risk, which is probably why you were told to avoid this.

It is not clear what you mean by "buried variable". If you mean a member of an object (or even accessible through a sequence), there is a greater risk in that since the object could be deleted, leaving you with an invalid reference. Since you cannot aim a reference later at something else, there's no way for you to indicate that it is invalid, so a pointer might be better.

There is no problem with the const.

Can you explain what it is that you are trying to do or give more context?

Uri
+4  A: 

I can't think of a reason why const float & would be better than const float.

References make sense if you're either worried about copies being made (which is irrelevant with a primitive type like float) or you want to be able to update a value across all instances that share the reference (which is irrelevant with const members).

On top of that, references in members are a huge pain in the neck* when it comes to initialization, and so they'd have to offer a significant advantage of the alternatives in order to be useful, and it's clearly not the case with const float.


* The FQA on references is always amusing and thought provoking

Assaf Lavie
Now that you put it *that* way ... (:
jw
TomWij
references is what you have to use if you want to do operator overloading with value types. (and for other types too, of course, since you can't overload them on pointers. they've already got builtin meanings)
Johannes Schaub - litb
+2  A: 

The address of a float may be bigger than the float itself (e.g., on 64 bits PCs).
Also it's faster to computing something with a float directly than with the address of a float (you don't have to dereference it), but maybe compilers can optimize that.

Bastien Léonard
So you are saying that in general, using a reference will cause a pointer to be used? I would think any marginally smart compiler would make a copy of a float. (but what do I know :)
jw
j w, if what you want is a alias, you don't have a choice. you will have to use a reference. performance considerations are secondary, but on performance,i would expect the copy way be faster than using a reference
Johannes Schaub - litb
but it could well be the other way around.. probably depends much on the compiler used (i.e if it can figure out where the ref refs to, it can completely elide out the ref, ...).
Johannes Schaub - litb
+4  A: 

You should only use references in the following cases: (If I didn't forgot one)

  1. The type being referred is not small and causes performance problems.
  2. You want your local alias to update when the value being referred to is updated.
    Or: You don't want to make a copy.
  3. You want the ability to update the other value. (In case it is not constant)

So in this case the float is small enough, you probably don't want it to update when the value being referenced to updates (causing problems in your calculations) and since you are using constants you don't want it to update.

So, you would want:

const float x = some.buried.variable.elsewhere;
TomWij
Yes, in my case I did not really want updates to the original to affect my variable, which is a case where my way is worse. Thanks!
jw
Finding the border lines between value, reference and pointer can sometimes be annoying, as well for const and not to forget the border between tests, asserts and exceptions.
TomWij
+1  A: 

There is no reason why that reference would be wrong or bad. If what you want is a local short-cut name ("alias"), references are the way to go. If you copy the float into a new variable, then you will get a new object and as you pointed out, of course you will also get a new address: Not at all what you would have wanted. So take a reference for that:

float &x = some.buried.variable.elsewhere;

Which will behave as the original variable in expressions.

However, as with everything, hard-coded rules won't do it. It depends on the specific situation. If you don't care about the object, but rather about the object's value, then create a copy. It's better to use the float directly than have that possible indirection through a reference when all you are interested in is to get easy access to the value of something.

What is bad is to use a reference when storing the result of a computation - because what is clearly of interest is the value only. You couldn't have any interest in a temporary float identity anyway, because it doesn't exist in the first place: A temporary float is not an object. It isn't even required to have any address (which can happen if the compiler keeps the float in a register):

float const &x = 3.0; // non-sense. don't use reference here

Same with the result of a function call of course

float const &x = get_some_float(); // non-sense too.
Johannes Schaub - litb
It seems that last bit isn't quite right for the case of const references - see Vincent's reply and link to Herb Sutter...
jw
Johannes Schaub - litb
see this answer for how to use it: http://stackoverflow.com/questions/455518/how-many-and-which-are-the-uses-of-const-in-c/455535#455535
Johannes Schaub - litb
much less so if you have a float even, where you can't have polymorphism with virtual of course :). The point i was making is when you have a temporary, there is no benefit having reference to having a copy directly (if the copy is a reasonable alternative, of course. in that article, it's not)
Johannes Schaub - litb
@litb, using a reference to a float can incur a performance penalty when the reference is used later in code. Using a reference for a basic type doesn't really have any advantage at all over using a copy.
Dan Olson
Johannes Schaub - litb
Dan, of course it doesn't. but if he wants it to have the same address as the other variable (like he showed with the assert), a copy just won't do it. as i said, if he doesn't want to have those properties that involve the object (but is rather interested in the value), a reference isn't it :)
Johannes Schaub - litb
if he just wants to use "x" instead of having to type foo.bar.baz.x, and he doesn't care change the other object, then he is better off using a copy, of course.
Johannes Schaub - litb
i've probably formulated the answer in a confusing way. trying to reformulating it later.
Johannes Schaub - litb
Johannes Schaub - litb
@litb, fair enough with respect to the address, but taking the address of floating point numbers is also bad practice in many (but not all) situations. :)
Dan Olson
+1  A: 

You do not need to worry so much about stack use, especially for a single float.

If you declare a variable on the stack, nothing says that the compiler has to actually create space for it unless you take its address. Even then, if the compiler can show that the variable's address isn't used, or if the compiler can see all the uses of that address (if the function calls that get passed the address are all visible) then it still doesn't have to actually create space for the variable.

One case I can see for using the reference instead of a copy is if the value is changed through the other name, perhaps in a function call, then the reference value will change also. With a copy it wouldn't.

Zan Lynx
+1  A: 

You mentioned that you thought the compiler might avoid allocating stack space for the reference. This may be true if you have optimization enabled, but the compiler could also optimize "const float x" by keeping the value in a FP register instead of on the stack. This is what GCC does on x86, for example.

Nathan Kitchen
+1  A: 

Short answer: this code is correct, keep it this way!

Long answer:

The problem with storing the return value of a function in a reference is that you are not allocating the space to actually store the return value, so you often end up with a reference pointing to a temporary value, either the temporary return value of the function (automatically created by the compiler) or a local variable allocated during the function (and freed at its end).

This is true that you must not return a reference on a local variable because this variable will be freed when the function ends.

But the C++ standard states that the compiler must not free the temporary return value of a function if it is "const referenced". So if your function is returning a float by value, you can store this value in float reference, the compiler ensures that the actual temporary value returned by the function will not be freed until you are done with your reference.

Note that this behaviour is only valid for "const" references only.

Take a look at this Herb Sutter's article for more explanation.

Vincent Robert
+1. Great article. Had I asked, I would mark this as the correct answer.
David Rodríguez - dribeas
I -1'd because while it's good to know about, it's not applicable to the OP's situation, nor with floats in general. I would argue that the code is NOT correct the way he wrote it and that this answer is bad advice when taken in the context of returning primitive or integral types.
Dan Olson
A: 

Something to keep in mind: a reference-to-const doesn't necessarily mean the thing you're referencing won't change at some point. It just means you will not change the referred-to data through that reference.

So someone could theoretically change the value of some.buried.variable.elsewhere and then reading x would reflect that updated value. (Subject to the usual aliasing rules, etc, etc. Essentially, it would be updated the next time the value of x was read.)

(Feel free to comment if I'm getting anything wrong, here, I'll update or delete the answer.)

leander
A: 

If you question is generalized so that float is an arbitrary type (and not a POD), then

const T& x = some.buried.variable.elsewhere;

could generally be a good thing as you can use x in the following code rather than some.buried.variable.elsewhere (presumably x will have a meaningful name).

Note that you specifically said variable. If instead your code was some.buried.method() then I would caution against using const T& because of the possibility of your readers being confused (the mere fact that you have to think about what happens to the return value in this case is reason enough to avoid the situtation).

Dan
+1  A: 

Rewritten for clarity:

References are pointers in disguise with some added syntactic sugar. Pointers have any number of performance and efficiency problems. Aliasing is one notable example. The compiler can't guarantee that the memory underneath the pointer or reference is the same as the last time it read it, because any old pointer can go through and modify it. The compiler is forced to re-read the value from memory on every use rather than caching it in memory.

On most processors registers are fast, memory accesses are not. So we never want to access memory if we can avoid it. Primitive types (int, float, etc) often go into registers. The compiler has complete control over what data resides in each register and can guarantee that nothing will overwrite the value, so variables of primitive types can potentially stay in registers for a long time until the compiler needs to write any changes back out to memory.

So when dealing with primitive types, it's often less efficient to use a reference. It disallows the compiler from caching the value in a register, inserts hidden dereferences, and can open you up to performance issues arising from aliasing.

Perhaps the efficiency isn't important to you in this case, but you said you couldn't think of a good reason not to use the reference other than saving typing. I submit there is a very good reason.

Best practice with this in mind, in my opinion, would be to use references primarily for aggregate types. The compiler can't pass these around in a single register so the relative cost of using the reference is going to be heavily mitigated. If the type in question is a primitive type, I would always prefer to work with a copy of the value rather than a reference.

Dan Olson