Коммутативный оператор перегрузки + 2 разных объектов

У меня есть 2 класса, которые представляют собой матрицу:
1. RegularMatrix - представление O(n^2)
2. SparseMatrix - матрица, представленная в виде связанного списка (без нулей).

скажем, у меня есть:

RegularMatrix a;
SparseMatrix b;

я хочу иметь возможность:

a+b;

а также:

b+a;

поэтому я перегружаю оператор +. Мой вопрос: поскольку я хочу, чтобы сложение было коммутативным (a + b = b + a), мне нужно реализовать 2 перегрузки, по одной для каждого случая?

RegularMatrix operator+(const RegualarMatrix &, const SparseMatrix &);
RegularMatrix operator+(const SparseMatrix & ,const RegualarMatrix &);

или есть общая форма, которую компилятор определяет сам?

Спасибо


person Asher Saban    schedule 21.09.2010    source источник


Ответы (3)


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

RegularMatrix operator+(const SparseMatrix &a, const RegualarMatrix &b) {
    return b + a;
}
person Johannes Schaub - litb    schedule 21.09.2010
comment
Спасибо .. И еще одна вещь, которая меня интересует, считается ли лучшим программированием возвращать ссылку RegularMatrix, чем возвращать объект по значению? - person Asher Saban; 22.09.2010
comment
@dmckee: Честно говоря, это было больше минуты. :) - person sbi; 22.09.2010
comment
@sbi: Согласен. Но я получил уведомление, как только потянулся за мышью, чтобы опубликовать свое... - person dmckee --- ex-moderator kitten; 22.09.2010
comment
@dmckee: я тебя слышу. То же самое произошло со мной сразу после того, как я написал вам этот комментарий... - person sbi; 22.09.2010

Требуются обе версии, просто напишите после первой перегрузки:

RegularMatrix operator+(const SparseMatrix &a, const RegualarMatrix &b)
{
    return operator+(b,a);
}

или более простая версия:

RegularMatrix operator+(const SparseMatrix &a, const RegualarMatrix &b)
{
    return b + a;
}
person yanpas    schedule 03.01.2016

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

#include <iostream>    // std::cout
#include <utility>     // std::pair
#include <type_traits> // std::remove_cvref_t
#include <concepts>    // std::same_as

// These two utilities will be used for all commutative functions:

template <typename T, typename U, typename V, typename W>
concept commutative =
    (std::same_as<std::remove_cvref_t<T>, V> && std::same_as<std::remove_cvref_t<U>, W>) ||
    (std::same_as<std::remove_cvref_t<U>, V> && std::same_as<std::remove_cvref_t<T>, W>);

template <typename V, typename W, typename T, typename U>
requires commutative<T, U, V, W>
constexpr decltype(auto) order (T && a, U && b) {
    if constexpr (std::same_as<std::remove_cvref_t<T>, V>)
        return std::pair{std::forward<T>(a), std::forward<U>(b)};
    else
        return std::pair{std::forward<U>(b), std::forward<T>(a)};
}

// Here goes the use-case:

struct A {
    int aval;
};

struct B {
    int bval;
};

// This template declaration allows two instantiations:
// (A const &, B const &) and (B const &, A const &)
template <typename T, commutative<T, A, B> U>
A operator + (T const & first, U const & second) {
    // But now we need to find out which is which:
    auto const & [a, b] = order<A, B>(first, second);
    return {.aval = a.aval + b.bval};
}

// Just to test it:
int main () {
    A a = {.aval = 1};
    B b = {.bval = 2};

    A c = a + b;
    A d = b + a;

    std::cout << c.aval << '\n';
    std::cout << d.aval << '\n';
}

Если template <typename T, commutative<T, A, B> U> и auto const & [a, b] = order<A, B>(first, second); меньше стандартного кода, чем повторение всего определения с return b + a; для вашего сценария, то я думаю, это может быть полезно.

person egst    schedule 09.07.2021