Elegant solution to duplicate, const and non-const, getters?

C++Constants

C++ Problem Overview


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?

C++ Solutions


Solution 1 - C++

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&>(const_cast<const Foo*>(this)->get());
    }
};

Solution 2 - C++

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".

Solution 3 - C++

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

Solution 4 - C++

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++).

Solution 5 - C++

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

Solution 6 - C++

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.

Solution 7 - C++

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.

Another coding guideline that I have seen used in some teams is:-

  • If a member variable needs to be modified outside the class, always return a pointer to it via a non-const member function.
  • No member functions can return non-const references. Only const references are allowed form const member functions.

This allows some consistency in the overall codebase and the caller can clearly see which calls can modify the member variable.

Solution 8 - C++

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)
};

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMichaelView Question on Stackoverflow
Solution 1 - C++CAdakerView Answer on Stackoverflow
Solution 2 - C++Steve JessopView Answer on Stackoverflow
Solution 3 - C++Matthew FlaschenView Answer on Stackoverflow
Solution 4 - C++swonguView Answer on Stackoverflow
Solution 5 - C++JayView Answer on Stackoverflow
Solution 6 - C++markh44View Answer on Stackoverflow
Solution 7 - C++AbhayView Answer on Stackoverflow
Solution 8 - C++MatthewView Answer on Stackoverflow