• Advertisement
Sign in to follow this  

What are constant references used for?

This topic is 1335 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

In C++, is there an actual usage for constant references, or is it there for completeness(and perhaps to add ambiguity.)?

I would simply use constant variable or a preprocessor substitution to a literal value. Edited by gasto

Share this post


Link to post
Share on other sites
Advertisement

A const reference can reference both const and non-const objects, but you can't take a non-const reference to a const object, so there's no backdoor to modifying a const object. At least not without further subversion of the type system.


It would be difficult to imagine modifying a literal like 1 or the address of the first character of "Hello". In C that is undefined behaviour if allowed(if I recall correctly).

Share this post


Link to post
Share on other sites

Read-only visibility into an object is also a common use case, i.e. returning a const reference from a function.


Would it refer to anything? I mean, after the execution of the function, local variables with automatic block duration would be wiped out of memory, hence the reference returned by such hypothetical function would refer to garbage.

Share this post


Link to post
Share on other sites

Literals aren't really a relevant consideration.  Think of references as pointers to some location in memory that holds some value.  That's essentially what they are; syntactic sugar on top of a pointer, with a few semantic bonuses to help the compiler understand what you're attempting to do and thereby do it efficiently, or tell you it can't be done and give a reasonable explanation as to why.

 

So if there's a chunk of memory that has a value, and you have a reference to that memory, then it wouldn't be that hard to imagine modifying that value.  Marking the reference as const just helps to protect you from your own forgetfulness, so that you don't accidentally modify something that other pieces of code are assuming won't be modified naively.

 

Note that if you ever happen to pass a literal value to a function that has a const reference parameter, the literal itself isn't what the reference refers to.  Instead, the literal gets copied/pushed onto the call stack as a function parameter, and thus that parameter is stored in memory and has an address.  (There might be optimizations whereby the literal is instead copied directly into a register and never stored to memory, but this probably only ever happens if the compiler knows for certain that you never use the reference in a way that would depend on the parameter being in memory.  In which case, the fact that the parameter is a const reference is in effect ignored by the compiler, since it didn't matter.)

Share this post


Link to post
Share on other sites

Would it refer to anything? I mean, after the execution of the function, local variables with automatic block duration would be wiped out of memory, hence the reference returned by such hypothetical function would refer to garbage.

Local variables, yes. But member variables of a class that hasn't gone out of scope would persist.

Look at std::vector, the const versions of the [] operator or the at function return a const reference to the requested index.

Share this post


Link to post
Share on other sites

but this probably only ever happens if the compiler knows for certain that you never use the reference in a way that would depend on the parameter being in memory.


How wouldn't it? I mean, 5 is always going to be 5 and it is known at compile time.

Share this post


Link to post
Share on other sites

5 and "hello" are literals.

int and std::string are variables.

int& and std::string& are reference variables.

 

It's mostly pointless to pass an int by const reference because it is so small, but it is very useful to pass a std::string by const reference. Passing a std::string by value means you have to copy it. Passing a std::vector<std::string> by value means every element in the vector will be copied.

 

It is very useful to be able to say, "I don't want to copy this variable, but I want to pass read-only access to it." - this means either a const reference or a const pointer. It serves a really useful purpose.

 

Example:

ByteBuffer lotsOfData = LoadFile("myfile.data");
 
DoSomethingWithTheData(lotsOfData);

I'd much rather 'DoSomethingWithTheData()' takes the buffer by const reference than copying all the bytes.

 

[Edit:] Ah, perhaps you are meaning Type & const, instead of const Type & ? Yeah, that's pointless (and I think invalid code). But const Type & is definitely valuable.

Edited by Servant of the Lord

Share this post


Link to post
Share on other sites

 

A const reference can reference both const and non-const objects, but you can't take a non-const reference to a const object, so there's no backdoor to modifying a const object. At least not without further subversion of the type system.


It would be difficult to imagine modifying a literal like 1 or the address of the first character of "Hello". In C that is undefined behaviour if allowed(if I recall correctly).

 

 

You can't without subverting the type system -- and that's the point. But when you alias an object by using a different name -- say, with a pointer or reference -- the constness of that new name is carried from the new name, not the object. That's why the compiler enforces it this way -- the new name can be more restrictive (a const reference or const pointer can alias either a const or non-const object), but never less restrictive (which is why a non-const reference or non-const pointer can't alias a const object). You can always further subvert the type system until you have a non-const alias to a const object, and when you do the compiler will happily accept it and your program will not-so-happily crash when you attempt to modify it (typically. I don't know whether its undefined or unspecified, specifically, but its not legal code). 

 

Diving a little deeper on a related issue, its something of a misnomer in C or C++ to say that you have a "const int" -- the platform might (or might not) actually protect the memory that stores that int with some mechanism, or it might place it in the .TEXT segment rather than .DATA, but the languages don't require that. It would be more accurate to think of it as having an "int-sized memory location whose name carries and enforces the qualities of a const value". The compiler will do its best to keep constness transitory when you alias the original name or address with a new name, but this is C and C++ where the programmer is god -- there are always ways around.

Share this post


Link to post
Share on other sites

I think the confusion is that the OP is confusing 'const' with literal. const does not mean a compile time constant. There are certain places where that is the case, but in the general case, it's just a type modifier that says that a particular variable can't be modified (without subverting the type system, etc...).

 

In a function: 'const char*' doesn't need to point to a compile-time string that's embedded in the executable. It can point to anything, anywhere. (It can even be re-assigned to point to other strings. Those could be variables you allocated on the stack, or dynamically, or they could be literals. The only thing that const means in that sense is that you can't use that pointer to modify the thing it's pointing it. That's it. Period.

 

Now re-read that paragraph and substitute reference for pointer. (With the exception that you can't re-assign the reference to point to something new.) It all applies. You can still construct a const reference to anything you want, regardless of how it was created/allocated. You just can't use that reference to modify the value.

Share this post


Link to post
Share on other sites

const TYPE & VAR
#3: To indicate the difference between access on read and access on write. In the following example, we have an orientation class with a position (type CVector3). In order to avoid updating the matrix every time it is read, we want to set a dirty flag only when it is written.
 

enum {
    ORIENTATION_POS_DIRTY = (1 << 0),
    ORIENTATION_SCALE_DIRTY = (1 << 1),
    ORIENTATION_ROT_DIRTY = (1 << 2),
    ORIENTATION_NORMALIZE = (1 << 3),
};
// Read version.
const CVector & COrientation::Pos() const {
    // No dirty flags; just return the value for read-only.
    return m_vPos;
}
// Write version.
CVector & COrientation::Pos() {
    m_ui32Dirty |= ORIENTATION_POS_DIRTY;  // Setting this bit indicates that later we need to re-update our matrix.
    return m_vPos;
}
m_aChar.Pos() += m_aPlatform.Pos();  // Only character’s position vector was accessed for write; it will cause a matrix rebuild for the character, not for the platform. 


How will the compiler discern between one overloaded function and the other? Both have the same signature.

Share this post


Link to post
Share on other sites

[Edit:] Ah, perhaps you are meaning Type & const, instead of const Type & ? Yeah, that's pointless (and I think invalid code). But const Type & is definitely valuable.


That is true. However, sneak-peeking on other's people interpretation is useful.

Share this post


Link to post
Share on other sites

How will the compiler discern between one overloaded function and the other? Both have the same signature.

The non-const overload is chosen for non-const objects/references, and the const overload is chosen for const objects/references.

Share this post


Link to post
Share on other sites

How will the compiler discern between one overloaded function and the other? Both have the same signature.


const CVector & COrientation::Pos() const {
CVector & COrientation::Pos() {
The difference in the signature is the const at the end of the line. As Brother Bob mentioned, if the variable is declared const, the version with the const at the end will be called.

Share this post


Link to post
Share on other sites

 

How will the compiler discern between one overloaded function and the other? Both have the same signature.

In addition to the explanation given by Brother Bob and reiterated by Rattrap, I showed a different example:
m_aChar.Pos() += m_aPlatform.Pos();
Neither m_aChar nor m_aPlatform are const objects in this context (neither were originally declared with const).

Instead, one is being accessed for read and the other for write.
When possible, the compiler will always choose the const method if it exists.

If only the const method existed, the line would be invalid because you couldn’t write to m_aChar.
If only the non-const method existed, both m_aChar.Pos() and m_aPlatform.Pos() would go through the non-const version (which would set a dirty flag on m_aPlatform and needlessly cause an update to its matrix).

Since both exist, the compiler will try to use const for both m_aChar and m_aPlatform, but it will realize that m_aChar is being accessed for write, so it instead must use the non-const version. m_aPlatform is accessed for read so the default (the const version) remains as the compiler’s choice.


L. Spiro

It doesn't matter whether you write to the returned value or just read from it; the function that is called (the const or non-const vatiant) is determined exclusively based on the object it is called on. The non-const is called when possible, and the const is called when necessary.

Share this post


Link to post
Share on other sites

How will the compiler discern between one overloaded function and the other? Both have the same signature.

In addition to the explanation given by Brother Bob and reiterated by Rattrap, I showed a different example:
m_aChar.Pos() += m_aPlatform.Pos();
Neither m_aChar nor m_aPlatform are const objects in this context (neither were originally declared with const).

Instead, one is being accessed for read and the other for write.
When possible, the compiler will always choose the const method if it exists.

If only the const method existed, the line would be invalid because you couldn’t write to m_aChar.
If only the non-const method existed, both m_aChar.Pos() and m_aPlatform.Pos() would go through the non-const version (which would set a dirty flag on m_aPlatform and needlessly cause an update to its matrix).

Since both exist, the compiler will try to use const for both m_aChar and m_aPlatform, but it will realize that m_aChar is being accessed for write, so it instead must use the non-const version. m_aPlatform is accessed for read so the default (the const version) remains as the compiler’s choice.


L. Spiro

It doesn't matter whether you write to the returned value or just read from it; the function that is called (the const or non-const vatiant) is determined exclusively based on the object it is called on. The non-const is called when possible, and the const is called when necessary.


My head is spinning. Is this a C++ standard feature? From what I recall, a function's signature only includes its name and parameters. Is a reference return type considered a parameter? Edited by gasto

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement