2

I have two versions of operator():

const A& operator(int x ,int y) const;
A& operator(int x ,int y);

But I don't know what would be the best way to avoid code repetition (because they essentially have the same logic).

I did read the awesome FAQ regarding operator overloading (and some other questions) but did not find answer.

EDIT_1: I've tried the suggestion that Skizz proposed but something like that does not quite work for me but maybe I've missed something:

Because this works while this does not compile giving the following error:

error: invalid initialization of reference of type ‘int&’ from expression of type ‘const value_type {aka const int}’

SadStudent
  • 287
  • 1
  • 4
  • 15

3 Answers3

2

If both methods 'essentially have the same logic' then surely they should both be marked const. If they do behave differently, should they be called the same thing?

What you need is a third, hidden method that does the common thing, like this:-

const A& operator(int x ,int y) const
{
  return Common (x,y);
}
A& operator(int x ,int y)
{
  return Common (x,y);
}
A& Common (int x, int y) const
{
   // common code
}

This takes advantage of the implicit non-const to const conversion. In the first method, the this object remains const in the call to Common but the non-const return value is converted to const. In the second, the non-const this object is converted to a const this object and the return object is unchanged.

Skizz
  • 64,439
  • 10
  • 63
  • 105
  • They are the same in the sense that they both go and find an item at x,y coordinates but sometimes I'd like it to be a constant ref and sometimes not... – SadStudent Sep 09 '13 at 16:36
  • const matters here because the operators return references. One should be able to modify a(x,y)=5 if and only if a is non-const; one should be able to access z=a(x,y) regardless of whether a is const or not. – Michael Sep 10 '13 at 18:17
  • @Michael: In the original question, it's not clear if the operators are members of A or if A is a separate class. In the latter case, the constness of the operator function does not affect the constness or the return value. In the former case, yes, you'd need const and non-const operators. – Skizz Sep 11 '13 at 09:49
  • @Skizz I've tried doing what you proposed but it doesn't work, maybe I've missed something ? (I've edited the question with more info) – SadStudent Sep 11 '13 at 13:30
  • @SadStudent: You're returning a reference to a member so it has the same constness as the constness of the this pointer (the first of the two alternatives I mention in my previous comment). – Skizz Sep 12 '13 at 10:43
1

If you have the data in question as shared/unique pointer, you are able to forward it to a common function:

class X {
    public: 
    const A& operator () (int x ,int y) const { 
        common(data, x, y);
        return *data;
    }

    A& operator () (int x ,int y) { 
        common(data, x, y);
        return *data;
    }

    private:
    void common(std::shared_ptr<T>, int x ,int y) const;
    std::shared_ptr<A> data;
};

Now you are able to access the T* via data.get() which is 'T* get() const'. (Note: I consider the common function a bad design)

You might do, also:

class X {
    public:
    const A& operator () (int x ,int y) const { 
        common(x, y); 
        return data;
    }

    A& operator () (int x ,int y) { 
       // ... make mutations
       common(x, y); 
       // ... make mutations
       return data;
    }

    void common(std::shared_ptr<T>, int x ,int y) const;
    T data;
};

Or actually, making the data mutable, if it is not breaking logical const:

class X {     
    const A& operator () (int x ,int y) const { 
        // ... make mutations on the mutable data, only
        return *lazy_evaluated_data;
    }

    A& operator () (int x ,int y) { 
        const X& self = *this;
        self(x, y);
        return *lazy_evaluated_data;;
    }

    private:
    mutable T lazy_evaluated_data; // or a cache
};
  • Doesn't the second operator make a `const A&` i.e a const reference into a non const reference ? – SadStudent Sep 09 '13 at 15:35
  • @SadStudent No, but the shared poinnter has the function 'T* get() const' –  Sep 09 '13 at 15:37
  • OK but it's a plain vector anyway so I guess it's not relevant :\ And what would you consider a good solution for this problem? I can think of some bad ones too :) – SadStudent Sep 09 '13 at 15:42
  • Don't make data mutable for coding convenience. This may cause unintended data modifications from other seemingly const functions. – Michael Sep 10 '13 at 18:19
  • @Michael I know - it may be a cache –  Sep 10 '13 at 18:43
0

Write a helper function and call it from both operators. You'll need to do some const_cast<...>(this) which can be ugly, but it's a common pattern.

cdmh
  • 3,167
  • 1
  • 21
  • 40
  • 1
    Doing `const_cast<...>(this)` is more than ugly - [it could lead to undefined behavior](http://stackoverflow.com/q/7349559/335858). – Sergey Kalinichenko Sep 09 '13 at 15:20
  • @cdmh as the previous commenter said I tend to avoid const_cast in a similar way I avoid fire :) so if there's some work around that would not use const_cast it'd be nice – SadStudent Sep 09 '13 at 15:37
  • 1
    @SadStudent that's generally good advice, but nothing is so bad that it should be avoided without good reason. See duplicate answer at http://stackoverflow.com/questions/123758/how-do-i-remove-code-duplication-between-similar-const-and-non-const-member-func which references Scott Meyers' solution using `const_cast` AND `static_cast` together ;) – cdmh Sep 09 '13 at 16:10