tags:

views:

884

answers:

9

Don't you hate it when you have

class Foobar {
public:
    Something& getSomething(int index) {
        // big, non-trivial chunk of code...
        return something;
    }

    const Something& getSomething(int index) const {
        // big, non-trivial chunk of code...
        return something;
    }
}

We can't implement either of this methods with the other one, because you can't call the non-const version from the const version (compiler error). A cast will be required to call the const version from the non-const one.

Is there a real elegant solution to this, if not, what is the closest to one?

Duplicate: http://stackoverflow.com/questions/123758/how-do-i-remove-code-duplication-between-similar-const-and-non-const-member-funct

+7  A: 

I would cast the const to the non-const (second option).

Matthew Flaschen
It would work, but i don't consider this very elegant...
Benoît
@Benoît: No, not elegant, but pragmatic.
Johann Gerell
+1: I think this is one of the few valid uses of const_cast - it definitely beats code duplication and/or jumping through all sorts of hoops.
James Hopkin
I am not arguing against that :)
Benoît
+5  A: 

Try to eliminate the getters by refactoring your code. Use friend functions or classes if only a very small number of other things needs the Something.

In general, Getters and Setters break encapsulation because the data is exposed to the world. Using friend only exposes data to a select few, so gives better encapsulation.

Of course, this is not always possible so you may be stuck with the getters. At the very least, most or all of the "non-trivial chunk of code" should be in one or more private functions, called by both getters.

markh44
What?! Getters and setters break encapsulation? And friends don't? What if the class inner structure (private part) changes, the variables are renamed or eliminated? do you rewrite all friends? -1 from me...
Paulius Maruška
Yes, friends are really too unused. Lots of C++ devs see them as breaking encapsulation, when they instead (by words from Herb Sutter) actually increase encapsulation.
Johann Gerell
Paulius: Imagine you have 2 classes that use each others data. What is the best way to allow access to that data? public getters and setters or making them friends? With public getters and setters, any piece of code could use those getters and setters. With friends, only the friends can access the data. The scope of who can see and/or modify the data is hugely reduced, so yes, friend can give better encapsulation than getters and setters. I really don't know why people panic when friend is mentioned. friend is good.
markh44
friend is good when the classes are actually good mates. friend is terrible if the classes barely know one another, or if another class comes along that wants to access the data, so you just add another friend. C++ != Facebook, and classes shouldn't respond to friend requests. I'd say if the classes weren't designed together, don't use friend. Once that's out of the way, yes, friend is good :-)
Steve Jessop
Of course I agree and should have made clear in my answer that the classes involved in friendship must be very strongly related. It's not something that should be used for convenience (neither should getters and setters for that matter).
markh44
Indeed, if you're implementing a non-const getter based on an integer index, then what that says to me is operator[] and/or an iterator. Admit that you're a container, basically. But they still have the same const/non-const code duplication issue.
Steve Jessop
The only way I could agree to this, is if you make a private getters and setters, and then let friends use those... Accessing private data members of a class can not increase encapsulation in any possible way... Not in my world...
Paulius Maruška
This explains it better than I could: http://www.parashift.com/c++-faq-lite/friends.html#faq-14.2
markh44
+1  A: 

The concept of 'const' is there for a reason. To me it establishes a very important contract based on which further instructions of a program are written. But you can do something on the following lines :-

  1. make your member 'mutable'
  2. make the 'getters' const
  3. return non-const reference

With this one can use a const reference on the LHS if you need to maintain the const functionality where you are using the getter along with the non-const usage(dangerous). But the onus is now on the programmer to maintain class invariants.

As has been said in SO before, casting away constness of an originally defined const object and using it is an U.B. So i would not use casts. Also making a non-const object const and then again casting away constness would not look too good.

Abhay
A: 

The const reference to the object makes sense (you're putting a restriction on read-only access to that object), but if you need to allow a non-const reference, you might as well make the member public.

I believe this is a la Scott Meyers (Efficient C++).

swongu
Public members? Not very OO-ish...
Michael
I might retract this, because I didn't read the part about having "non-trivial code", making those functions not getters directly.
swongu
Actually, when you think of it. If not misused, public members are the simplest and most straight-forward answer to this problem.If anyone can manipulate them by using the proper getter/setter, then in effect it is a public member.Maybe making it public is even better code because it tells the client, "I'm a public member, do anything you want to me".It is much more straight-forward than implementing two types of getters.
Michael
"you might as well make the member public". No, because there's a "big, non-trivial chunk of code" in the getter. This getter isn't just exposing a member, it's doing significant work. Whether it's a good idea to then expose the result as non-const is of course another question...
Steve Jessop
+5  A: 

I recall from one of the Effective C++ books that the way to do it is to implement the non-const version by casting away the const from the other function.

It's not particularly pretty, but it is safe. Since the member function calling it is non-const, the object itself is non-const, and casting away the const is allowed.

class Foo
{
public:
    const int& get() const
    {
        //non-trivial work
        return foo;
    }

    int& get()
    {
        return const_cast<int&>(static_cast<const Foo*>(this)->get());
    }
};
CAdaker
It's safe provided that foo isn't (and never becomes) a const member. If it does, then you ought to re-implement the non-const version of get (or go back to the drawing board and not make foo const after all), but what you actually do is fail to notice the problem because it all compiles OK.
Steve Jessop
"a const member". Or a const return from a call to some other function. Basically the compiler thinks it's returned as const, but "really" it's returned as non-const because of the cast. So it has to really be non-const in the case where "this" is non-const, and the compiler can't help you enforce that.
Steve Jessop
Of course, the normal rules for const_cast still apply: if foo was defined as const it's not legal to cast it away. This is just for the case where both methods are valid, but we don't want to repeat code.
CAdaker
Sure, but hand-validating the const-correctness might be harder than just c'n'p'ing the code and keeping it in sync with changes. Or it might be easier. So it's important to be aware of the risk.
Steve Jessop
But the problem of const-correctness doesn't disappear anyway. If you duplicate the code from the const method you still have a non-const member function returning a non-const reference.This would be exactly the same thing, and casting away the const will be perfectly valid if the copy-and-paste method would be valid.
CAdaker
Right, but if you c'n'p, the compiler will tell you whether the code is valid. If you cast, you have to work it out for yourself. I'm not saying this is impossible, or that it's necessarily a bad idea, but C++ programmers do tend to lean on the compiler, especially for const-correctness and other forms of type safety. That is what it's there for.
Steve Jessop
Ok, that is a point. But the most common case is probably that we have a lot of validation and error checking code, which should be easy to check.
CAdaker
A: 

How about:

template<typename IN, typename OUT>
OUT BigChunk(IN self, int index) {
    // big, non-trivial chunk of code...
    return something;
}

struct FooBar {
    Something &getSomething(int index) {
        return BigChunk<FooBar*, Something&>(this,index);
    }

    const Something &getSomething(int index) const {
        return BigChunk<const FooBar*, const Something&>(this,index);
    }
};

Obviously you'll still have object code duplication, but no source code duplication. Unlike the const_cast approach, the compiler will check your const-correctness for both versions of the method.

You probably need to declare the two interesting instantiations of BigChunk as friends of the class. This is a good use of friend, since the friend functions are hidden away close to the friendee, so there is no risk of unconstrained coupling (ooh-er!). But I will not attempt the syntax for doing so right now. Feel free to add.

Chances are that BigChunk needs to deference self, in which case the above order of definition isn't going to work very well, and some forward declarations will be needed to sort it out.

Also, in order to avoid some numpty finding BigChunk in the header and deciding to instantiate and call it even though it's morally private, you can move the whole lot into the cpp file for FooBar. In an anonymous namespace. With internal linkage. And a sign saying "beware of the leopard".

Steve Jessop
+1  A: 

I'm not sure where something is coming from, but if it's not a member of the class, but rather something that you're looking up somewhere, it's possible that you could implement it like getSomethingLocal below:

class Foobar {
private:
    Something &getSomethingLocal(int index) const
    {
        //  blah blah blah
        return something;
    }

public:
    Something& getSomething(int index) {
        return getSomethingLocal(index);
    }

    const Something& getSomething(int index) const {
        return getSomethingLocal(index);
    }
}
Eclipse
+1  A: 

Why not just pull the common code out into a separate, private function, and then have the other two call that?

A: 

I dare suggest using the preprocessor:

#define ConstFunc(type_and_name, params, body) \
    const type_and_name params const body \
    type_and_name params body

class Something
{
};

class Foobar {
private:
    Something something;

public:
    #define getSomethingParams \
    ( \
        int index \
    )

    #define getSomethingBody \
    { \
        return something; \
    }

    ConstFunc(Something & getSomething, getSomethingParams, getSomethingBody)
};
Matthew