tags:

views:

172

answers:

5

What is a good rule of thumb as to whether or not the return value of a const member function should be const or not? Here's what I tend to do, but I struggle with ambiguous scenarios.

class foo
{
    public:
        // if the returned object is conceptually
        // (and therefore usually literally but not even necessarily) 
        // owned by *this*,
        // then a const ptr should be returned
        const ownedByFoo *getOwnedByFoo() const { return m_ownedByFoo; }
        ownedByFoo *getOwnedByFoo() { return m_ownedByFoo; }

        // what's a poor boy to do here?
        // veryRelatedToFoo might be a conceptual contemporary or parent
        // of Foo. Note naming the function "find..." instead of "get.."
        // since *get* implies the object holds it as a data member but
        // that may not even be the case here. There could be a
        // standalone container that associates foo and veryRelatedToFoo.
        // Should constness be symmetrical here?
        const veryRelatedToFoo *findVeryRelatedToFoo() const;
        veryRelatedToFoo *findVeryRelatedToFoo();

        // If the returned object is only (conceptually)
        // peripherally related to *this*, 
        // the returned object probably shoudn't be const, even if
        // foo happens to have one of these as a direct data member,
        // since we don't want to give away the implementation details of
        // Foo and it may hold peripherallyRelatedToFoo simply for performance
        // reasons, etc.
        peripherallyRelatedToFoo *findPeripherallyRelatedToFoo() const

    ...
};

One additional note is that once you have asymmetrical constness, you could ask const object A to return object B, then ask object B to return object A, and then you've managed to bypass the intended constness of object A.

+1  A: 

Having any constness at all is more than you'll find in many languages!

Once you understand (as you have) the difference between 'physically const' and 'logically const', then the 'ambiguous' scenarios remain just that: subject to debate. Consider for example even the fact that the mutable keyword exists.

I think you have the right general idea: and beyond that it's all debatable and subjective and varies according to the particular problem and constraints etc.

I tend to prefer 'no exceptions to the rule', so I'd avoid returning a pointer-to-non-const from a const method; but I suppose I can imagine some scenarios where I might be tempted ... though I might instead be more likely to decide to make the method non-const.

ChrisW
+1  A: 

(I'm ignoring the fact that you are returning object state directly like that, which is usually a bad idea because it breaks encapsulation -- I'm looking at this as "sample code")

There is no relation between the two. A const member function is a member that doesn't modify the underlying object -- "while it's executing". There is no promise or requirement that the return value be const or not. The const-ness contract is "over", once the method returns. I think you're getting confused because you are trying to connect the return value with the method that produced it.

Whether the returned value should be const or not const depends exclusively on the nature of the returned value, and the semantics that you want to ascribe to the method.

In your specific first case, the two methods should return the same const-ness. The const overloading is probably not what you want to use. If you want the two variations, I would probably make it explicit, like such:

const ownedByFoo *getOwnedByFoo() const { return m_ownedByFoo; }
ownedByFoo *getOwnedByFooForEdit() { return m_ownedByFoo; }

That way there is no ambiguity and no mysterious tying of the two. Note that I made the second non-const because we probably don't want client code to modify m_ownedByFoo on a const object. That's a requirement born out of the semantic of the method ("returns internal state"), not out of a linkage between the method's const-ness and the const-ness of the return value. If the value being returned was not part of the object's state, I would probably do something else.

Euro Micelli
Technically, you're right wrt the const-ness contract, but that's an overly literal interpretation. When you ask a object innocently (as in const) to hand over a piece of itself, and you get (a loaded gun in the form of) a non-const object that you can then modify to your hearts content? That method is a trojan horse into your object.Your 2nd pt is good. I think people are generally used to the overloaded signature, because they can use the same get method anywhere, in const and non-const situations, without having to think about it, but making the caller think about it is often a good thing
no-op
@no-op, Sure, I agree and I find that the literal interpretation prevents confusion. An argument can be made that you walked into that trap by "providing a loaded gun" to begin with (a pointer to a non-const of your state). I might suggest either: a) Consider always returning a const* (make that your semantics). b) Consider providing a copy of the actual state; specify that in your semantics and pay the corresponding copy cost. c) Consider making the const overload private, so the method cannot be called on a const object.
Euro Micelli
... I personally find it too obscure for the const'ness of the return value to depend on the const'ness of your object. YMMV.
Euro Micelli
A: 

Do not return a non-const pointer or reference to member data from a const method.

JohnMcG
A: 

If the returned reference is const, make the function const, if it's not, don't. You should try to avoid returning a non-const reference to member data though because that breaks encapsulation, unless the class is primarily some kind of container class.

markh44
A: 

If returning a non-const reference/pointer would allow the caller to invalidate an invariant that the class should be maintaining, then don't return that non-const reference/pointer. (It should be const.)

Thanatos