views:

678

answers:

4

For example, suppose I have a class:

class Foo
{
public:
    std::string& Name()
    {
        m_maybe_modified = true;
        return m_name;
    }

    const std::string& Name() const
    {
        return m_name;
    }
protected:
    std::string m_name;
    bool m_maybe_modified;
};

And somewhere else in the code, I have something like this:

Foo *a;
// Do stuff...
std::string name = a->Name(); // <-- chooses the non-const version

Does anyone know why the compiler would choose the non-const version in this case?

This is a somewhat contrived example, but the actual problem we are trying to solve is periodically auto-saving an object if it has changed, and the pointer must be non-const because it might be changed at some point.

+11  A: 

Two answers spring to mind:

  1. The non-const version is a closer match.

  2. If it called the const overload for the non-const case, then under what circumstances would it ever call the non-const overload?

You can get it to use the other overload by casting a to a const Foo *.

Edit: From C++ Annotations

Earlier, in section 2.5.11 the concept of function overloading was introduced. There it noted that member functions may be overloaded merely by their const attribute. In those cases, the compiler will use the member function matching most closely the const-qualification of the object:

"Annoations" --> "Annotations"
Jonathan Leffler
Doh. Thanks, fixed.
+16  A: 

Because a is not a const pointer. Therefore, a non-const function is a closer match. Here is how you can call the const function:

const Foo* b = a;
std::string name = b->Name();

If you have both a const and a non-const overload, and want to call the const one on a non-const object, this might be an indication of bad design.

Lev
The full design is hard to express in an example, but there are a few points that could be improved, I'm sure...We currently do the const pointer method, but it is ugly, and more importantly relies on the programmer to remember to do so.
Caleb Huitt - cjhuitt
How about giving the const overload a different name? Or having both a const overload and a differently named function, one calling the other in-line?
Lev
Just a nit to pick... the pointer isn't const... it's a "pointer to const" in your example.
Pat Notz
+2  A: 

The compiler does not take into account how you are using the return value in its determination; that's not part of the rules. It doesn't know if you're doing

std::string name = b->Name();

or

b->Name() = "me";

It has to choose the version that works in both cases.

Mark Ransom
Whoever dinged me on this answer, care to tell me why?
Mark Ransom
Correct Answer +1
Martin York
The question wasn't about the constness or not of the return value, it was about the constness of the object. The -1 wasn't me though.
I understand the potential problems for the compiler, but I would think that it would be able to make some simple checks such as in the example, where it would be immediately using the return for a copy and then discarding it.
Caleb Huitt - cjhuitt
That would be pretty dangerous, as a change to some other line of code would cause the behavior of that particular call to change as well. The rules for determining overload resolution are complex enough as it is.
Michael Burr
A: 

You can add a "cName" function that is equivalent to "Name() const". This way you can call the const version of the function without casting to a const object first.

This is mostly useful with the new keyword auto in C++0x, which is why they are updating the library to include cbegin(), cend(), crbegin(), crend() to return const_iterator's even if the object is non-const.

What you are doing is probably better done by having a setName() function that allows you to change the name rather than returning a reference to the underlying container and then "maybe" it is modified.

Greg Rogers
I realize that it might be better with a setName() function, but again, the example was somewhat contrived. In actuallity, we have pointers objects containing other objects, and would prefer the const pointers be returned where possible, when strung together as "int c = a->B()->GetC();", etc.
Caleb Huitt - cjhuitt