I'm wondering with functions like the following, whether to use a temporary variable (p):
void parse_foo(const char*& p_in_out,
foo& out) {
const char* p = p_in_out;
/* Parse, p gets incremented etc. */
p_in_out = p;
}
or can I just use the original argument and expect it to be optimized similarly to the above anyway? It seems like there should be such an optimization, but I've seen the above done in a few places such as Mozilla's code, with vague comments about "avoiding aliasing".
All good answers, but if you're worried about performance optimization, the actual parsing is going to take nearly all of the time, so pointer aliasing will probably be "in the noise".
The variant with a temporary variable could be faster since it doesn't imply that every change to the pointer is reflected back to the argument and the compiler has better chances on generating faster code. However the right way to test this is to compile and look at the disassembly.
Meanwhile this has noting to do with avoiding aliasing. In fact, the variant with a temporary variant does employ aliasing - now you have two pointers into the same array and that's exactly what aliasing is.
I would use a temporary if there is a possibility that the function is transactional.
i.e. the function succeeds or fails completely (no middle ground).
In this case I would use a temp to maintain state while the function executes and only assign back to the in_out parameter when the function completes successfully.
If the function exits prematurely (ie via exception) then we have two situations:
With a temporary (the external pointer is unchanged)
Using the parameter directly the external state is modified to reflect position.
I don't see any optimization advantages to either method.
Yes, you should assign it to a local that you mark restrict (__restrict in MSVC).
The reason for this is that if the compiler cannot be absolutely sure that nothing else in the scope points at p_in_out, it cannot store the contents under the pointer in a local register. It must read the data back every time you write to any other char * in the same scope. This is not an issue of whether it is a "smart" compiler or not; it is a consequence of correctness requirements.
By writing char* __restrict p you promise the compiler that no other pointer in the same scope points to the same address as p. Without this guarantee, the value of *p can change any time any other pointer is written to, or it may change the contents of some other pointer every time *p is written to. Thus, the compiler has to write out every assignment to *p back to memory immediately, and it has to read them back after every time another pointer is written through.
So, guaranteeing the compiler that this cannot happen — that it can load *p exactly once and assume no other pointer affects it — can be an improvement in performance. Exactly how much depends on the particular compiler and situation: on processors subject to a load-hit-store penalty, it's massive; on most x86 CPUs, it's modest.
The reason to prefer a pointer to a reference here is simply that a pointer can be marked restrict and a reference cannot. That's just the way C++ is.
You can try it both ways and measure the results to see which is really faster. And if you're curious, I've written in depth on restrict and the load-hit-store elsewhere.
addendum: after writing the above I realize that the people at Moz were more worried about the reference itself being aliased -- that is, that something else might point at the same address where const char *p is stored, rather than the char to which p points. But my answer is the same: under the hood, const char *&p means const char **p, and that's subject to the same aliasing issues as any other pointer.
How does the compiler know that p_in_out isn't aliased somehow? It really can't optimize away writing the data back through the reference.
struct foo {
setX(int); setY(int);
const char* current_pos;
} x;
parse_foo(x.current_pos, x);
I look at this and ask why you didn't just return the pointer Then you don't have a reference to a pointer and you don't have to worry about modify the original.
const char* parse_foo(const char* p, foo& out) {
//use p;
return p;
}
It also means you can call the function with an rvalue:
p = parse_foo(p+2, out);
One thought that comes immediately in mind: exception safety. If you throw an exception during parsing, the use of a temporary variable is what you should do to provide strong exception safety: Either the function call succeeded completely or it didn't do anything (from a user's perspective).
Related
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Sell me on const correctness
What is the usefulness of keyword const in C or C++ since it's allowed such a thing?
void const_is_a_lie(const int* n)
{
*((int*) n) = 0;
}
int main()
{
int n = 1;
const_is_a_lie(&n);
printf("%d", n);
return 0;
}
Output: 0
It is clear that const cannot guarante the non-modifiability of the argument.
const is a promise you make to the compiler, not something it guarantees you.
For example,
void const_is_a_lie(const int* n)
{
*((int*) n) = 0;
}
#include <stdio.h>
int main()
{
const int n = 1;
const_is_a_lie(&n);
printf("%d", n);
return 0;
}
Output shown at http://ideone.com/Ejogb is
1
Because of the const, the compiler is allowed to assume that the value won't change, and therefore it can skip rereading it, if that would make the program faster.
In this case, since const_is_a_lie() violates its contract, weird things happen. Don't violate the contract. And be glad that the compiler gives you help keeping the contract. Casts are evil.
In this case, n is a pointer to a constant int. When you cast it to int* you remove the const qualifier, and so the operation is allowed.
If you tell the compiler to remove the const qualifier, it will happily do so. The compiler will help ensure that your code is correct, if you let it do its job. By casting the const-ness away, you are telling the compiler that you know that the target of n is non-constant and you really do want to change it.
If the thing that your pointer points to was in fact declared const in the first place, then you are invoking undefined behavior by attempting to change it, and anything could happen. It might work. The write operation might not be visible. The program could crash. Your monitor could punch you. (Ok, probably not that last one.)
void const_is_a_lie(const char * c) {
*((char *)c) = '5';
}
int main() {
const char * text = "12345";
const_is_a_lie(text);
printf("%s\n", text);
return 0;
}
Depending on your specific environment, there may be a segfault (aka access violation) in const_is_a_lie since the compiler/runtime may store string literal values in memory pages that are not writable.
The Standard has this to say about modifying const objects.
7.1.6.1/4 The cv-qualifiers [dcl.type.cv]
Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior
"Doctor, it hurts when I do this!" "So don't do that."
Your...
int n = 1;
...ensures n exists in read/write memory; it's a non-const variable, so a later attempt to modify it will have defined behaviour. Given such a variable, you can have a mix of const and/or non-const pointers and references to it - the constness of each is simply a way for the programmer to guard against accidental change in that "branch" of code. I say "branch" because you can visualise the access given to n as being a tree in which - once a branch is marked const, all the sub-branches (further pointers/references to n whether additional local variables, function parameters etc. initialised therefrom) will need to remain const, unless of course you explicitly cast that notion of constness away. Casting away const is safe (if potentially confusing) for variables that are mutable like your n, because they're ultimately still writing back into a memory address that is modifiable/mutable/non-const. All the bizarre optimisations and caching you could imagine causing trouble in these scenarios aren't allowed as the Standard requires and guarantees sane behaviour in the case I've just described.
Sadly it's also possible to cast away constness of genuinely inherently const variables like say const int o = 1;, and any attempt to modify them will have undefined behaviour. There are many practical reasons for this, including the compiler's right to place them in memory it then marks read only (e.g. see UNIX mprotect(2)) such that an attempted write will cause a CPU trap/interrupt, or read from the variable whenever the originally-set value is needed (even if the variable's identifier was never mentioned in the code using the value), or use an inlined-at-compile-time copy of the original value - ignoring any runtime change to the variable itself. So, the Standard leaves the behaviour undefined. Even if they happen to be modified as you might have intended, the rest of the program will have undefined behaviour thereafter.
But, that shouldn't be surprising. It's the same situation with types - if you have...
double d = 1;
*(int*)&d = my_int;
d += 1;
...have you have lied to the compiler about the type of d? Ultimately d occupies memory that's probably untyped at a hardware level, so all the compiler ever has is a perspective on it, shuffling bit patterns in and out. But, depending on the value of my_int and the double representation on your hardware, you may have created an invalid combination of bits in d that don't represent any valid double value, such that subsequent attempts to read the memory back into a CPU register and/or do something with d such as += 1 have undefined behaviour and might, for example, generate a CPU trap / interrupt.
This is not a bug in C or C++... they're designed to let you make dubious requests of your hardware so that if you know what you're doing you can do some weird but useful things and rarely need to fall back on assembly language to write low level code, even for device drivers and Operating Systems.
Still, it's precisely because casts can be unsafe that a more explicit and targeted casting notation has been introduced in C++. There's no denying the risk - you just need to understand what you're asking for, why it's ok sometimes and not others, and live with it.
The type system is there to help, not to babysit you. You can circumvent the type system in many ways, not only regarding const, and each time that you do that what you are doing is taking one safety out of your program. You can ignore const-correctness or even the basic type system by passing void* around and casting as needed. That does not mean that const or types are a lie, only that you can force your way over the compiler's.
const is there as a way of making the compiler aware of the contract of your function, and let it help you not violate it. In the same way that a variable being typed is there so that you don't need to guess how to interpret the data as the compiler will help you. But it won't baby sit, and if you force your way and tell it to remove const-ness, or how the data is to be retrieved the compiler will just let you, after all you did design the application, who is it to second guess your judgement...
Additionally, in some cases, you might actually cause undefined behavior and your application might even crash (for example if you cast away const from an object that is really const and you modify the object you might find out that the side effects are not seen in some places (the compiler assumed that the value would not change and thus performed constant folding) or your application might crash if the constant was loaded into a read-only memory page.
Never did const guarantee immutability: the standard defines a const_cast that allows modifying const data.
const is useful for you to declare more intent and avoid changing data that is you meant to be read only. You'll get a compilation error asking you to think twice if you do otherwise. You can change your mind, but that's not recommended.
As mentionned by other answers the compiler may optimize a bit more if you use const-ness but the benefits are not always significant.
I know that it improves readability and makes the program less error-prone, but how much does it improve the performance?
And on a side note, what's the major difference between a reference and a const pointer? I would assume they're stored in the memory differently, but how so?
[Edit: OK so this question is more subtle than I thought at first.]
Declaring a pointer-to-const or reference-of-const never helps any compiler to optimize anything. (Although see the Update at the bottom of this answer.)
The const declaration only indicates how an identifier will be used within the scope of its declaration; it does not say that the underlying object can not change.
Example:
int foo(const int *p) {
int x = *p;
bar(x);
x = *p;
return x;
}
The compiler cannot assume that *p is not modified by the call to bar(), because p could be (e.g.) a pointer to a global int and bar() might modify it.
If the compiler knows enough about the caller of foo() and the contents of bar() that it can prove bar() does not modify *p, then it can also perform that proof without the const declaration.
But this is true in general. Because const only has an effect within the scope of the declaration, the compiler can already see how you are treating the pointer or reference within that scope; it already knows that you are not modifying the underlying object.
So in short, all const does in this context is prevent you from making mistakes. It does not tell the compiler anything it does not already know, and therefore it is irrelevant for optimization.
What about functions that call foo()? Like:
int x = 37;
foo(&x);
printf("%d\n", x);
Can the compiler prove that this prints 37, since foo() takes a const int *?
No. Even though foo() takes a pointer-to-const, it might cast the const-ness away and modify the int. (This is not undefined behavior.) Here again, the compiler cannot make any assumptions in general; and if it knows enough about foo() to make such an optimization, it will know that even without the const.
The only time const might allow optimizations is cases like this:
const int x = 37;
foo(&x);
printf("%d\n", x);
Here, to modify x through any mechanism whatsoever (e.g., by taking a pointer to it and casting away the const) is to invoke Undefined Behavior. So the compiler is free to assume you do not do that, and it can propagate the constant 37 into the printf(). This sort of optimization is legal for any object you declare const. (In practice, a local variable to which you never take a reference will not benefit, because the compiler can already see whether you modify it within its scope.)
To answer your "side note" question, (a) a const pointer is a pointer; and (b) a const pointer can equal NULL. You are correct that the internal representation (i.e. an address) is most likely the same.
[update]
As Christoph points out in the comments, my answer is incomplete because it does not mention restrict.
Section 6.7.3.1 (4) of the C99 standard says:
During each execution of B, let L be any lvalue that has &L based on P. If L is used to
access the value of the object X that it designates, and X is also modified (by any means),
then the following requirements apply: T shall not be const-qualified. ...
(Here B is a basic block over which P, a restrict-pointer-to-T, is in scope.)
So if a C function foo() is declared like this:
foo(const int * restrict p)
...then the compiler may assume that no modifications to *p occur during the lifetime of p -- i.e., during the execution of foo() -- because otherwise the Behavior would be Undefined.
So in principle, combining restrict with a pointer-to-const could enable both of the optimizations that are dismissed above. Do any compilers actually implement such an optimization, I wonder? (GCC 4.5.2, at least, does not.)
Note that restrict only exists in C, not C++ (not even C++0x), except as a compiler-specific extension.
Off the top of my head, I can think of two cases where proper const-qualification allows additional optimizations (in cases where whole-program analysis is unavailable):
const int foo = 42;
bar(&foo);
printf("%i", foo);
Here, the compiler knows to print 42 without having to examine the body of bar() (which might not be visible in the curent translation unit) because all modifications to foo are illegal (this is the same as Nemo's example).
However, this is also possible without marking foo as const by declaring bar() as
extern void bar(const int *restrict p);
In many cases, the programmer actually wants restrict-qualified pointers-to-const and not plain pointers-to-const as function parameters, as only the former make any guarantees about the mutability of the pointed-to objects.
As to the second part of your question: For all practical purposes, a C++ reference can be thought of as a constant pointer (not a pointer to a constant value!) with automatic indirection - it is not any 'safer' or 'faster' than a pointer, just more convenient.
There are two issues with const in C++ (as far as optimization is concerned):
const_cast
mutable
const_cast mean that even though you pass an object by const reference or const pointer, the function might cast the const-ness away and modify the object (allowed if the object is not const to begin with).
mutable mean that even though an object is const, some of its parts may be modified (caching behavior). Also, objects pointed to (instead of being owned) can be modified in const methods, even when they logically are part of the object state. And finally global variables can be modified too...
const is here to help the developer catch logical mistakes early.
const-correctness generally doesn't help performance; most compilers don't even bother to track constness beyond the frontend. Marking variables as const can help, depending on the situation.
References and pointers are stored exactly the same way in memory.
This really depends on the compiler/platform (it may help optimisation on some compiler that has not yet been written, or on some platform that you never use). The C and C++ standards say nothing about performance other than giving complexity requirements for some functions.
Pointers and references to const usually do not help optimisation, as the const qualification can legally be cast away in some situations, and it is possible that the object can be modified by a different non-const reference. On the other hand, declaring an object to be const can be helpful, as it guarantees that the object cannot be modified (even when passed to functions that the compiler does not know the definition of). This allows the compiler to store the const object in read-only memory, or cache its value in a centralised place, reducing the need for copies and checks for modifications.
Pointers and references are usually implemented in the exact same way, but again this is totally platform dependant. If you are really interested then you should look at the generated machine code for your platform and compiler in your program (if indeed you are using a compiler that generates machine code).
One thing is, if you declare a global variable const, it may be possible to put it in the read-only portion of a library or executable and thus share it among multiple processes with a read-only mmap. This can be a big memory win on Linux at least if you have a lot of data declared in global variables.
Another situation, if you declare a constant global integer, or float or enum, the compiler may be able to just put the constant inline rather than using a variable reference. That's a bit faster I believe though I'm not a compiler expert.
References are just pointers underneath, implementation-wise.
It can help performance a little bit, but only if you are accessing the object directly through its declaration. Reference parameters and such cannot be optimized, since there might be other paths to an object not originally declared const, and the compiler generally can't tell if the object you are referencing was actually declared const or not unless that's the declaration you are using.
If you are using a const declaration, the compiler will know that externally-compiled function bodies, etc. cannot modify it, so you get a benefit there. And of course things like const int's are propagated at compile time, so that's a huge win (compared to just an int).
References and pointers are stored exactly the same, they just behave differently syntactically. References are basically renamings, and so are relatively safe, whereas pointers can point to lots of different things, and are thus more powerful and error-prone.
I guess the const pointer would be architecturally identical to the reference, so the machine code and efficiency would be the same. the real difference is syntax -- references are a cleaner, easier to read syntax, and since you don't need the extra machinery provided by a pointer, a reference would be stylistically preferred.
I'm wondering which one is better of these two implementations of passing addresses to pointers. Is there a data exchange in the 1st one that doesn't happen in the 2nd one? Is the second one a more efficient way? More readable? both the same?
Version 1
void ptrFunction(int& arg)
{
int* ptr = &arg;
std::cout<<"The pointer's value:" << *ptr << "\n";
}
int main()
{
int x=5;
ptrFunction(x);
return 0;
}
Version 2
void CallViaPointers(int *arg)
{
int *ptr = *arg;
std::cout << "The pointer's value: " << *ptr;
}
int main()
{
int x = 100;
CallViaPointers(&x);
return 0;
}
In terms of efficiency, the two will almost certainly compile down to the same code behind-the-scenes, as references are often implemented as automatically-dereferenced pointers. From that perspective, the two solutions are equivalent.
As for whether to pass by pointer or by reference, that's really a judgment call that depends on the situation. If what you're trying to do is just print out a pointer to the integer (as you're doing here), then pass-by-pointer makes a bit more sense because it's more explicitly aligned with what you're trying to do. In general, write the function so that the parameter is of the type you want internally. If you need a pointer - either because you're building a linked structure or because you're doing some sort of memory allocation - then take in a pointer. If you're dealing with polymorphic classes, then traditionally you would accept your argument by pointer rather than by reference, though both work. If you're taking in a stack-allocated object to try to modify it, then the reference can be a bit clearer. And, if you're in one of those situations where you must use a reference - for example, as a parameter to an overloaded operator, copy constructor, or assignment operator - then of course go with the reference. Readability is really undervalued these days, but making the effort to have clean, readable, intuitive code is definitely worth the effort.
Performance wise, it depends entirely on the implementation, of course. Generally, they will compile to the same machine code. And this is definitely one of those cases where you should not think about optimizations at all unless you have to. Readability is much more important in most code.
As for readability, I tend to prefer version 2, because I want the user of my function to know that I'm going to (or able to) modify whatever he puts in there, but there are exceptions to this rule (such as when catching exceptions, or for operator overloading).
I'd say it depends on what the function does. Right now your function name doesn't match what it does. If it did, it'd be named print_pointer and it'd be clear it should just take a pointer directly. (I read the question incorrectly. But yes, it still depends on what your function does.)
Let's say you have a function that prints a pointer, like:
void print_pointer(void* ptr)
{
std::cout << ptr << std::endl;
}
It doesn't make sense for this function to be written to take a reference, then get the address of it, because you don't want an object, you want a pointer to an object, and null is valid in this context.
If, however, you need an object, like your code does, you should take a reference, because pointing to that object is an implementation detail; the interface, with a reference, clearly documents you need an object to refer to.
As it stands, version two is broken, because passing null crashes the program, yet null is a valid pointer value. Don't advertise you want a pointer value when it isn't the case. To fix it, check for null and do something clearly documented, like nothing or throw an exception. But best it to change the parameter to a reference, because you leave those issues to the caller, and your function works regardless.
(If someone has a pointer to an object, they can be responsible for dereferencing it safely to pass a valid object to your function. In other words, using a reference is self-documenting: "null isn't valid here".)
Performance is irrelevant.
They are equivalent, and this can be verified by compiling and checking the disassembly - it's identical other than symbol names.
As for style, it's mostly a personal preference, but there is a common pattern of using a pointer for out-parameters. This is because when you take a reference, it's not clear if the function you're calling is going to modify it. "Effective C++" uses const references for in-parameters and non-const pointers for out-parameters. I find this a good way to make it clear what the contract is.
int *ptr = *arg;
Should be:
int *ptr = arg;
I find the reference parameter more readable than the pointer parameter when it is not clear the parameter will be used as a pointer somewhere else. This is usually the case, and use of parameters is often encapsulated so callers don't care about implementation details. However, you may have to conform to an existing interface, and that may be using pointers as iterators – then you're "passing an iterator by value" (the usual way to pass an iterator) rather than "passing a pointer to an object", even if that iterator is actually a raw pointer type. (Other types of existing interfaces are possible.)
Beyond the above, personal preference, and one allowing null pointers (you cannot have a null reference), they are identical in every way.
I'm moving from Java to C++ and am a bit confused of the language's flexibility. One point is that there are three ways to store objects: A pointer, a reference and a scalar (storing the object itself if I understand it correctly).
I tend to use references where possible, because that is as close to Java as possible. In some cases, e.g. getters for derived attributes, this is not possible:
MyType &MyClass::getSomeAttribute() {
MyType t;
return t;
}
This does not compile, because t exists only within the scope of getSomeAttribute() and if I return a reference to it, it would point nowhere before the client can use it.
Therefore I'm left with two options:
Return a pointer
Return a scalar
Returning a pointer would look like this:
MyType *MyClass::getSomeAttribute() {
MyType *t = new MyType;
return t;
}
This'd work, but the client would have to check this pointer for NULL in order to be really sure, something that's not necessary with references. Another problem is that the caller would have to make sure that t is deallocated, I'd rather not deal with that if I can avoid it.
The alternative would be to return the object itself (scalar):
MyType MyClass::getSomeAttribute() {
MyType t;
return t;
}
That's pretty straightforward and just what I want in this case: It feels like a reference and it can't be null. If the object is out of scope in the client's code, it is deleted. Pretty handy. However, I rarely see anyone doing that, is there a reason for that? Is there some kind of performance problem if I return a scalar instead of a pointer or reference?
What is the most common/elegant approach to handle this problem?
Return by value. The compiler can optimize away the copy, so the end result is what you want. An object is created, and returned to the caller.
I think the reason why you rarely see people do this is because you're looking at the wrong C++ code. ;)
Most people coming from Java feel uncomfortable doing something like this, so they call new all over the place. And then they get memory leaks all over the place, have to check for NULL and all the other problems that can cause. :)
It might also be worth pointing out that C++ references have very little in common with Java references.
A reference in Java is much more similar to a pointer (it can be reseated, or set to NULL).
In fact the only real differences are that a pointer can point to a garbage value as well (if it is uninitialized, or it points to an object that has gone out of scope), and that you can do pointer arithmetics on a pointer into an array.
A C++ references is an alias for an object. A Java reference doesn't behave like that.
Quite simply, avoid using pointers and dynamic allocation by new wherever possible. Use values, references and automatically allocated objects instead. Of course you can't always avoid dynamic allocation, but it should be a last resort, not a first.
Returning by value can introduce performance penalties because this means the object needs to be copied. If it is a large object, like a list, that operation might be very expensive.
But modern compilers are very good about making this not happen. The C++ standards explicitly states that the compiler is allowed to elide copies in certain circumstances. The particular instance that would be relevant in the example code you gave is called the 'return value optimization'.
Personally, I return by (usually const) reference when I'm returning a member variable, and return some sort of smart pointer object of some kind (frequently ::std::auto_ptr) when I need to dynamically allocate something. Otherwise I return by value.
I also very frequently have const reference parameters, and this is very common in C++. This is a way of passing a parameter and saying "the function is not allowed to touch this". Basically a read-only parameter. It should only be used for objects that are more complex than a single integer or pointer though.
I think one big change from Java is that const is important and used very frequently. Learn to understand it and make it your friend.
I also think Neil's answer is correct in stating that avoiding dynamic allocation whenever possible is a good idea. You should not contort your design too much to make that happen, but you should definitely prefer design choices in which it doesn't have to happen.
Returning by value is a common thing practised in C++. However, when you are passing an object, you pass by reference.
Example
main()
{
equity trader;
isTraderAllowed(trader);
....
}
bool isTraderAllowed(const equity& trdobj)
{
... // Perform your function routine here.
}
The above is a simple example of passing an object by reference. In reality, you would have a method called isTraderAllowed for the class equity, but I was showing you a real use of passing by reference.
A point regarding passing by value or reference:
Considering optimizations, assuming a function is inline, if its parameter is declared as "const DataType objectName" that DataType could be anything even primitives, no object copy will be involved; and if its parameter is declared as "const DataType & objectName" or "DataType & objectName" that again DataType could be anything even primitives, no address taking or pointer will be involved. In both previous cases input arguments are used directly in assembly code.
A point regarding references:
A reference is not always a pointer, as instance when you have following code in the body of a function, the reference is not a pointer:
int adad=5;
int & reference=adad;
A point regarding returning by value:
as some people have mentioned, using good compilers with capability of optimizations, returning by value of any type will not cause an extra copy.
A point regarding return by reference:
In case of inline functions and optimizations, returning by reference will not involve address taking or pointer.
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?
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
You should only use references in the following cases: (If I didn't forgot one)
The type being referred is not small and causes performance problems.
You want your local alias to update when the value being referred to is updated.
Or: You don't want to make a copy.
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;
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.
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.
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.
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.
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.
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.
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?
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.)