Умножение объекта на константу слева

У меня есть класс Matrix, и он имеет перегруженные операторы * для скалярного и матричного умножения.

template <class T> class Matrix
{
    public:
        // ...
        Matrix operator*(T scalar) const;
        // ...
}

// ...

template <class T>
Matrix<T> Matrix<T>::operator*(T RightScalar) const
{
    Matrix<T> ResultMatrix(m_unRowSize, m_unColSize);
    for (uint64_t i=0; i<m_unRowSize; i++)
    {
        for (uint64_t j=0; j<m_unColSize; j++)
        {
            ResultMatrix(i, j) = TheMatrix[m_unColSize * i + j] * RightScalar;
        }
    }
    return ResultMatrix;
}

// ...

Я могу без проблем умножить матричный объект на скаляр справа:

Matrix<double> X(3, 3, /* ... */);  // Define a 3x3 matrix and initialize its contents
Matrix<double> Y;                   // Define an output matrix
Y = X * 10.0;                       // Do the linear operation

Но как мне умножить его с левой стороны таким же образом?

Matrix<double> X(3, 3, /* ... */);
Matrix<double> Y;
Y = 10.0 * X;

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

Можно ли реализовать это на C++?
Если возможно, как мне изменить метод класса в моем коде?


person hkBattousai    schedule 23.01.2013    source источник
comment
Это возможно, но не с помощью метода класса.   -  person Kimi    schedule 23.01.2013


Ответы (2)


Функции-члены сопоставляются их левым аргументом, который является указателем this. Поскольку нативные типы не могут иметь функций-членов, вам нужно добавить правое умножение с пользовательскими типами через функции, не являющиеся членами (а также для других типов, к которым у вас нет доступа для записи).

template<typename T>
Matrix<T> operator*(T const& scalar, Matrix<T> rhs)
{
    // scalar multiplication is commutative: s M = M s
    return rhs *= scalar; // calls rhs.operator*=(scalar);
}

ПРИМЕЧАНИЕ. Вышеупомянутое не-членство operator* было реализовано с точки зрения члена operator*=. Рекомендуется записывать все умножения как функции, не являющиеся членами, и использовать член operator*= для реализации этих умножений с элементом матрицы lhs.

Это а) сохранит минимальный интерфейс класса и б) предотвратит скрытые преобразования. Например. у вас может быть класс Matrix, который неявно преобразуется в скаляр, если размеры равны 1x1, и эти преобразования могут происходить незаметно, если вы не предоставите отдельную перегрузку, которая является прямым совпадением.

template<typename T>
Matrix<T> operator*(Matrix<T> lhs, T const& scalar)
{
    return lhs *= scalar; // calls lhs.operator*=(scalar);
}

template<typename T>
Matrix<T> operator*(Matrix<T> lhs, Matrix<T> const& rhs)
{
    return lhs *= rhs; // calls lhs.operator*=(rhs);
}

Обратите внимание на то, что Матрица lhs является копией, а не ссылкой. Это позволяет компилятору выполнять такие оптимизации, как семантика копирования/перемещения. Также обратите внимание, что возвращаемый тип этих операторов — Matrix<T>, а не const Matrix<T>, который рекомендовался в некоторых старых книгах по C++, но который предотвращает семантику перемещения в C++11.

// class member 
template<typename T>
Matrix<T>& Matrix<T>::operator*=(Matrix<T> const& rhs)
{
    // your implementation
    return *this;
}

// class member 
template<typename T>
Matrix<T>& Matrix<T>::operator*=(T const& scalar)
{
    // your implementation
    return *this;
}
person TemplateRex    schedule 23.01.2013

Для этого вам понадобится функция, не являющаяся членом:

template <typename T>
Matrix<T> operator*(T scalar, Matrix<T> const & matrix) {
    return matrix * scalar;
}

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

person Mike Seymour    schedule 23.01.2013