Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to overload operator associativity in C++?

I'm building a class that has a slightly asymmetric addition. Before the complaints come in, it's necessarily asymmetric. There is a conversion (operation that takes a bit of time) that has to happen when two objects are added together, and the conversion most naturally happens with respect to the right summand.

To make this concrete, here's a generic example of what's going on...

class Foo {
    char _fav;
    int _prop;

public:
    const char fav() const {return _fav;}
    const int prop() const (return _prop;}
    void changeFav(char); // complicated method that also changes _prop
    void changeProp(int); // straightforward method
}

Foo
operator + (Foo A, Foo B) {
    Foo sum;
    if (A.fav() != B.fav()) A.changeFav(B.fav);
    sum.changeProp(A.prop() + B.prop());   
    return sum;
}

In order for two Foos to be added, they need to have the same _fav, so a choice must be made as to which to convert. Based on the details of Foo, it's most natural to change the left summand to match the right summand.

However, when doing:

Foo A,B,C;
Foo D = A + B + C; // D = (A + B) + C
Foo E = A + (B + C);

if A already has the same _fav as C, then changeFav is called twice for D (once to change A._fav to B._fav and then again to change (A+B)._fav to C._fav) and once for E (to change B._fav to C._fav). I prefer the latter but want to avoid forcing the user to use parentheses for multiple additions.

Is there a way to overload the associativity of operator + to make this happen?

like image 897
PengOne Avatar asked Nov 16 '25 18:11

PengOne


1 Answers

You can do a hack. You have to use another type to hold the intermediate results of the operation, then you can use an implicit cast to evaluate the result. Here is an example of how to implement Python-style comparison operators in C++:

#include <vector>
#include <cstdio>

struct S {
    S(int x) : val(x) { }
    int val;
};

struct Comparison {
    std::vector<S> operands;
    explicit Comparison(S x)
    {
        operands.push_back(x);
    }
    operator S()
    {
        auto i = operands.begin(), e = operands.end();
        S prev = *i;
        for (i++; i != e; i++) {
            S cur = *i;
            if (prev.val >= cur.val)
                return S(0);
            prev = cur;
        }
        return S(1);
    }
    void append(const Comparison &a)
    {
        operands.insert(
            operands.end(),
            a.operands.begin(),
            a.operands.end());
    }
    void append(const S &a)
    {
        operands.push_back(a);
    }
};

Comparison operator<(const Comparison &left, const Comparison &right)
{ Comparison result(left); result.append(right); return result; }
Comparison operator<(const Comparison &left, const S &right)
{ Comparison result(left); result.append(right); return result; }
Comparison operator<(const S &left, const Comparison &right)
{ Comparison result(left); result.append(right); return result; }
Comparison operator<(const S &left, const S &right)
{ Comparison result(left); result.append(right); return result; }

int main()
{
    S x(0);
    x = S(0) < S(1) < S(2) < S(3);
    std::printf("0 < 1 < 2 < 3 = %d\n", x.val);
    x = S(0) < S(1) < S(3) < S(2);
    std::printf("0 < 1 < 3 < 2 = %d\n", x.val);
    return 0;
}

In such a situation, however, I would be quick to ditch the + operator. I would avoid using + for any operation that is not associative and commutative, since that is the convention for + in mathematics. Instead, you can use a variadic function (using templates) to perform the desired computation.

like image 136
Dietrich Epp Avatar answered Nov 18 '25 09:11

Dietrich Epp



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!