Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is there no standardized way to avoid const method code duplication? [closed]

Tags:

c++

To my experience it is a common phenomenon that the same code is used in a const and a non const version of a member method. One way to avoid code duplication of complex methods is to use a const_cast to remove the const-ness in the non const version like Scott Meyers recommended in Effective C++ (item 3). However this is not beneficial in very short methods that may just return a pointer - of course the duplication is not that problematic in this case. Still this makes me wonder whether there is a reason that there is no keyword or something equivalent to replace the casting. I could imagine to use the following declaration:

autoconst Data* const getData() autoconst;

Of course this keyword would not add any functionality which was impossible to realize before but I think it would be nice to have. As far as I know the auto keyword similarly does not allow any new constructs but is a nice simplification in the code - admittedly much more extensive (please correct me if I'm wrong).

My question is whether this is in conflict with some rules in the C++ standard - and if not, whether it is just not useful enough to get implemented.

like image 200
w1th0utnam3 Avatar asked Feb 05 '15 19:02

w1th0utnam3


1 Answers

There is a standardized way, but not many people use it:

class X
{
    T data;
public:
    template<typename autoconst>
    friend /* or static */ auto get_data(autoconst& that) -> decltype(that.data)
    {
        // process that and return that.data
    }
};

It gets called as get_data(x) rather than x.get_data(), but one implementation serves both const and non-const usage, without casting or other un-typesafe techniques.

One can also have member functions to enable member call syntax. That will require const and non-const variants, but no duplication of the "process that.data" step, because both can delegate to the friend template.

More complete example:

template<typename T>
class HyperMatrix
{
    int rows, cols, planes;
    T* data;
    /* they get initialized somehow */

public:
    template<typename ThisType>
    friend /* or static */ auto at(ThisType& that, int const r, int const c, int const p) -> decltype(*(that.data))
    {
        // argument validation logic not being duplicated
        if (r < 0 || r >= that.rows) throw index_exception();
        if (c < 0 || c >= that.cols) throw index_exception();
        if (p < 0 || p >= that.planes) throw index_exception();
        // complicated indexing expression, also not duplicated
        const index = (p * that.rows + r) * that.cols + c;
        return that.data[index];
    }

    // these enable a more natural syntax than at(hymatrix, 1, 2, 3)
    T& operator()(int const r, int const c, int const p)
    { return /* ThisType = HyperMatrix<T> */ at(this, r, c, p); }

    const T& operator()(int const r, int const c, int const p)
    { return /* ThisType = const HyperMatrix<T> */ at(this, r, c, p); }
};

Example with no trivial workaround:

template<typename T>
class BalancedJumpTable
{
public:
    template<typename ThisType, typename Functor>
    friend /* or static */ auto for_each(ThisType& that, Functor f)
    {
        // complicated code for walking the tree not duplicated
        // deep inside loops and recursive calls, we find
           f(that->something());
        // maybe there's even some filtering logic which causes only
        // only certain items to be passed to the callback
    }

    template<typename Functor>
    void for_each(Functor f)
    { return for_each(this, f); }

    void for_each(Functor f) const
    { return for_each(this, f); }
};
like image 75
Ben Voigt Avatar answered Nov 10 '22 17:11

Ben Voigt