8

Mam klasę Matrix i przeciążono operatorów * dla multiplikacji skalarnych i macierzowych.Pomnożenie obiektu ze stałą od lewej strony

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

// ... 

mogę mnożyć obiekt Matrix z skalara z prawej strony bez problemu:

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 

Ale jak mogę pomnożyć ją z lewej strony ten sam sposób?

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

W arytmetyce jest to zwykła notacja, która zapisuje stałe po lewej stronie podczas mnożenia. Chciałbym przestrzegać tej zasady, aby mój kod był bardziej czytelny.

Czy można to zaimplementować w C++?
Jeśli to możliwe, w jaki sposób mogę zmodyfikować metodę klasy w moim kodzie?

+2

Jest to możliwe, ale nie za pomocą metody klasy. – Kimi

Odpowiedz

9

Funkcje członkowskie są dopasowywane przez ich argument po lewej stronie, który jest tym wskaźnikiem. Ponieważ typy natywne nie mogą mieć funkcji składowych, musisz dodać mnożenie z prawej strony z typami zdefiniowanymi przez użytkownika za pomocą funkcji, które nie są członkami (a także dla innych typów, do których nie masz uprawnień do zapisu).

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

UWAGA: Pisałem wyżej non-member operator* realizowane pod względem członka operator*=. Zaleca się zapisywanie wszystkich multiplikacji jako funkcji nieczłonkowskich i użycie elementu operator*= do implementacji tych multiplikacji za pomocą elementu Matrix lhs.

To spowoduje: a) zachowanie minimalnego interfejsu klasy i b) zapobieganie ukrytym konwersjom. Na przykład. możesz mieć klasę Matrix, która jest niejawnie przekształcalna w skalarną, jeśli wymiary wynoszą 1x1, a konwersje te mogą być w milczeniu, jeśli nie zapewnisz osobnego przeciążenia, które jest bezpośrednim dopasowaniem.

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

Informacja na temat tego, w jaki sposób macierz Lhs jest kopią, a nie odniesieniem. Pozwala to kompilatorowi na optymalizacje, takie jak semantyka kopiowania/przemieszczania. Zwróć też uwagę, że typem zwrotnym tych operatorów jest Matrix<T>, a nie const Matrix<T>, który był zalecany w niektórych starych książkach C++, ale który zapobiega semantykom przenoszenia w 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; 
} 
8

Musisz funkcję non-member aby to:

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

operator Non-member przeciążenia pozwalają na określenie wszelkiego rodzaju po obu stronach, a przeciążenia członkowskie zawsze uzyskać obiekt na lewej bok.

Powiązane problemy