Могу ли я переопределить операторы на С++?

Я знал, что мы можем перегрузить операторы для класса. Но мой вопрос: могу ли я переопределить операторов?

Давайте рассмотрим, что у меня есть базовый класс и производный класс, возможно ли переопределить оператор, определенный (перегруженный) в базовом классе в производном классе (как при переопределении функции)?

Ответ 1

Перегруженный оператор - это просто функция, поэтому она может быть виртуальной и переопределенной.

Но это редко хорошая идея.

Рассмотрим переопределенный оператор присваивания копии, который в каком-то производном классе проверяет, совместимо ли назначенное значение с назначенным объектом. Фактически он заменил проверку статического типа на динамическую проверку типов, что связано с большим количеством тщательного тестирования и статистической вероятностью правильности.


Пример непригодности:

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

Ответ 2

Вы можете добиться желаемого эффекта, предоставив виртуальную функцию "обложка" в базовом классе и вызовите ее из реализации оператора в базовом классе:

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

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

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

Демоверсия

Ответ 3

Я хочу добавить еще одну вещь: после моего личного разочарования по поводу поведения по умолчанию для некоторых операторов во встроенных типах я задался вопросом, можно ли было переопределить эти операторы простым и понятным способом. Ответ был мой проект Polyop, который достигает именно этого.

Итак, вы можете переопределить поведение по умолчанию операторов С++ по умолчанию? Да, просто оберните их так, чтобы операторский вызов был таким же, но тот факт, что он фактически называет совершенно другого оператора со свойствами и поведением, которые вы определили.

//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
}

Все без каких-либо сбоев производительности.