10

I knew that we can overload operators for a class. But my question is whether I can override operators?

Let us consider that I have a base class and derived class, is it possible to override an operator defined ( overloaded ) in the base class in the derived class (as with function overriding)?

Venkatesh
  • 1,537
  • 15
  • 28
  • 3
    Yes, you can do this. – Dwayne Towell Sep 26 '14 at 19:53
  • 1
    did you take a look at this: http://stackoverflow.com/questions/4421706/operator-overloading? – Erik Sep 26 '14 at 19:53
  • @Cyber I think this question is more specific than the one that you referenced. – Sergey Kalinichenko Sep 26 '14 at 19:53
  • 7
    My initial reaction when faced with a question like this would be to try it and see. – Logicrat Sep 26 '14 at 19:53
  • 7
    You can override operators, but they might not do what you want. The reason is that operators (actually overloads in general) are selected from the static type of an object. If you now have a reference-to-base which is bound to a derived instance, it will *not* call the operator for the derived class. If you want that, you will have to redirect the operator to a virtual function that then looks at the dynamic type of the object. – Ulrich Eckhardt Sep 26 '14 at 19:57
  • It is indeed correct, Alf, but please go ahead and explain why you think it is incorrect. The way it stands, your remark isn't constructive to further the understanding of the issue. – Ulrich Eckhardt Sep 26 '14 at 21:12
  • Not every function or operator is a class function that can be be virtual. In particular binary operators who's left side is a class you don't control are a case where you can only implement outside of a class. – Ulrich Eckhardt Sep 27 '14 at 06:56

3 Answers3

11

You can achieve the desired effect by providing a "cover" virtual function in the base class, and call it from the operator's implementation in the base class:

struct Base {
    Base operator+(const Base& other) {
        return add(other);
    }
protected:
    virtual Base add(const Base& other) {
        cout << "Adding in Base's code." << endl;
        return Base();
    }
};

struct Derived : public Base {
protected:
    virtual Base add(const Base& other) {
        cout << "Adding in Derived's code." << endl;
        return Derived();
    }
};

int main() {
    Base b1;
    Base b2;
    Derived d1;
    Derived d2;
    Base res;
    res = b1+b2; // Prints "Adding in Base's code."
    res = b1+d2; // Prints "Adding in Base's code."
    res = d1+b2; // Prints "Adding in Derived's code."
    res = d1+d2; // Prints "Adding in Derived's code."
    return 0;
}

Demo.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 4
    the [non-virtual interface](http://www.gotw.ca/publications/mill18.htm) is a pretty good idiom to use in general too! (and here it is even necessary) – TemplateRex Sep 26 '14 at 20:09
  • So I cannot override operators. I have to achieve that one by using the above method only right? – Venkatesh Sep 26 '14 at 20:10
  • 1
    @Venkatesh Although this is not the only possibility, it illustrates the general concept that your solution would end up following: the call would be a combination of a non-virtual operator in the base class and a virtual function implementing it in the derived class. – Sergey Kalinichenko Sep 26 '14 at 20:14
  • **-1** “you cannot "override" overloaded operators in the same sense that you can override virtual functions” is incorrect. – Cheers and hth. - Alf Sep 26 '14 at 20:32
10

An overloaded operator is just a function, so it can be virtual, and overridden.

But it's seldom a good idea.

Consider an overridden copy assignment operator, that in some derived class checks whether the value to be assigned is compatible with the object assigned to. Effectively it has replaced static type checking with dynamic type checking, which involves a lot of laborous testing and only a statistical chance of correctness.


Example of ungoodness:

#include <assert.h>
#include <iostream>
#include <string>
using namespace std;

struct Person
{
    string name;

    virtual
    auto operator=( Person const& other )
        -> Person&
    { name = other.name; return *this; }

    Person( string const& _name ): name( _name ) {}
};

struct Employee: Person
{
    int     id;

    auto operator=( Person const& other )
        -> Person&
        override
    {
        auto& other_as_employee = dynamic_cast<Employee const&>( other );

        Person::operator=( other );
        id =  other_as_employee.id;
        return *this;
    }

    auto operator=( Employee const& other )
        -> Employee&
    {
        return static_cast<Employee&>(
            operator=( static_cast<Person const&>( other ) )
            );
    }

    Employee( string const& _name, int const _id )
        : Person( _name )
        , id( _id )
    {}
};

void foo( Person& someone )
{
    someone = Person( "Maria" );        // Fails, probably unexpectedly.
}

auto main() -> int
{
    Person&& someone = Employee( "John", 12345 );
    foo( someone );
}
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Oh, I see I failed to leverage covariant result here. But instead of making the change for marginally improved code, I let it stand. After all it's an example of ungoodness. ;-) – Cheers and hth. - Alf Sep 26 '14 at 20:34
1

I want to add one more thing: After my personal frustration about the default behavior of certain operators on built-in types, I wondered if it was possible to override that operators in a simple and readable way. The answer was my Polyop project, which achieves exactly that.

So, can you override the default behavior of C++ operators? Yes, just wrap them in a way that the operator call seems to be the same, but the thing its actually calling a completely different operator with the properties and behavior you defined.

//Redefine the behavior of the int vs int equality operator
auto operator==(void(int,int) , pop::default_context )
{
    return [](int lhs , int rhs )
    {
        return lhs * 2 == rhs;
    };
}

using pop::triggers::_;

int main()
{
    bool equal = _(4) == 2; //Returns true, since its the behavior we defined above
}

All with no performance hits at all.

Manu343726
  • 13,969
  • 4
  • 40
  • 75
  • 1
    It seems there's something missing here? Anyway, after converting an integer to something else (boxing it, essentially) you can just define the relevant operator for the value wrapper. – Cheers and hth. - Alf Sep 27 '14 at 04:01
  • @Cheersandhth.-Alf of course its only wrapping. But the point of the library is to do that in a simple and transparent way. What you do is to redefine the operator for some combination of parameters (Thats specified with the first arg of the operator redefinition) and in what context that redefinition should be applied (The second arg, default in the example). The wrapper, `_()`, its only to trigger the library mechanisms. My point was to hide all the wrapper overloading to the user, only providing what he wants: Redefining the operator for an specific case. – Manu343726 Sep 27 '14 at 14:05
  • @Cheersandhth.-Alf also, what are you missing there? – Manu343726 Sep 27 '14 at 14:08