2013-10-16 330 views
0

我在寫矩陣模板類。一切順利,直到我超載乘法運算符。我的階級是這樣的:C++矩陣模板,矩陣矩陣和矩陣數相乘之間的模糊

template <typename TNum> class Matrix 
{ 
private: 
    // ... 
    TNum* Data; 
public: 
    const TMatIdx NRows; // Type TMatIdx defined somewhere else. 
    const TMatIdx NCols; 
    const TMatIdx Size; 

    // ... 
    // Matrix * matrix 
    template <typename T2> 
    const Matrix<TNum> operator*(const Matrix<T2>& right) const; 

    // Matrix * number 
    template <typename T2> 
    Matrix<TNum>& operator*=(const T2& scale); 
}; 

// Matrix * number 
template <typename TNum, typename T2> 
Matrix<TNum> operator*(Matrix<TNum> lhs, const T2& rhs); 
// Number * matrix 
template <typename TNum, typename T2> 
Matrix<TNum> operator*(const T2& lhs, Matrix<TNum> rhs); 

我希望能涵蓋矩陣和數字之間的所有可能的乘法組合具有相同*運營商。

然後我寫了乘以2 Matrix<double>秒的小測試程序,我鐺++編譯器抱怨歧義:

test.cpp:46: error: ambiguous overload for 'operator*' in 'M * N' 
matrix.h:225: note: candidates are: const QCD::Matrix<TNum> QCD::Matrix<TNum>::operator*(const QCD::Matrix<T2>&) const [with T2 = double, TNum = double] 
matrix.h:118: note:     QCD::Matrix<TNum> QCD::operator*(const T2&, QCD::Matrix<TNum>) [with TNum = double, T2 = QCD::Matrix<double>] 
matrix.h:109: note:     QCD::Matrix<TNum> QCD::operator*(QCD::Matrix<TNum>, const T2&) [with TNum = double, T2 = QCD::Matrix<double>] 
test.cpp:52: error: ambiguous overload for 'operator*' in 'M * N' 
matrix.h:225: note: candidates are: const QCD::Matrix<TNum> QCD::Matrix<TNum>::operator*(const QCD::Matrix<T2>&) const [with T2 = double, TNum = double] 
matrix.h:118: note:     QCD::Matrix<TNum> QCD::operator*(const T2&, QCD::Matrix<TNum>) [with TNum = double, T2 = QCD::Matrix<double>] 
matrix.h:109: note:     QCD::Matrix<TNum> QCD::operator*(QCD::Matrix<TNum>, const T2&) [with TNum = double, T2 = QCD::Matrix<double>] 

是否有可能克服這種模糊性,而不必明確寫下T2所有可能的特例?

,僅供參考,這裏是我的實現:

template<typename TNum> template <typename T2> 
Matrix<TNum>& Matrix<TNum> :: 
operator*=(const T2& rhs) 
{ 
    for(TMatIdx i = 0; i < Size; i++) 
     Data[i] *= rhs; 
    return *this; 
} 

template<typename TNum> template <typename T2> 
const Matrix<TNum> Matrix<TNum> :: 
operator*(const Matrix<T2>& right) const 
{ 
    Matrix<TNum> c(NRows, right.NCols); 
    TNum sum_elems; 
    for(TMatIdx i = 0; i < NRows; i++) 
    { 
     for(TMatIdx j = 0; j < right.NCols; j++) 
     { 
      sum_elems = TNum(0); 
      for(TMatIdx k = 0; k < right.NRows; k++) 
      { 
       sum_elems += at(i, k) * right.at(k, j); 
      } 

      c.at(i, j) = sum_elems; 
     } 
    } 
    return c; 
} 


template <typename TNum, typename T2> 
Matrix<TNum> operator*(Matrix<TNum> lhs, const T2& rhs) 
{ 
    lhs *= rhs; 
    return lhs; 
} 

template <typename TNum, typename T2> 
Matrix<TNum> operator*(const T2& lhs, Matrix<TNum> rhs) 
{ 
    rhs *= lhs; 
    return rhs; 
} 
+0

向我們展示乘法。 – 0x499602D2

回答

3

我將用C++ 11描述一個解決方案,然後解釋如何在C++ 98中實現它。

在C++ 11中,標題<type_traits>包含類型函數和類型謂詞。這使得強制約束更加方便。

std::is_same<T1, T2>::value如果T1是與T2相同的類型,則返回true,否則返回false。

typename std::enable_if< bool, T >::type是一個明確定義的類型T如果布爾是真實的,否則定義不明確。

當編譯器查找候選模板函數和方法時,如果嘗試的特化失敗,它不會引發錯誤。它只是拋出該候選人。這意味着下面的代碼將消除不確定性:

template <typename TNum, typename T2> 
typename std::enable_if< (!std::is_same<Matrix<TNum>, T2>::value), 
Matrix<TNum> >::type operator*(const T2& lhs, Matrix<TNum> rhs); 

只有做出這個決定時,聲明認爲。上面的邏輯在語義上是合理的,但是可以閱讀。所以C++ 11支持模板別名和constexpr函數。

template<bool B, typename T = void> 
using Enable_if = typename std::enable_if<B, T>::type; 

template<typename T1, typename T2> 
constexpr bool Is_same(){ 
    return std::is_same<T1, T2>::value; 
} 

以上就變成了:

template <typename TNum, typename T2> 
Enable_if<(!Is_same<Matrix<TNum>, T2>()), 
Matrix<TNum> > operator*(const T2& lhs, Matrix<TNum> rhs); 

概念將提供工具,使這個更方便。

現在,如果你沒有c + + 11,你不明白。但是,Boost提供了相同的功能。假設你也沒有,實施它們並不可怕。

編譯時函數依賴於多種語言規則,這使得它們難以理解。我們首先考慮enable_if。我們希望typename enable_if<true, T>::type明確定義,但typename enable_if<false, T>::type是無意義的。我們使用專業化:

template<bool B, typename T = void> 
struct enable_if { 
    typedef T type; 
}; 

template<typename T> 
struct enable_if<false, T> {}; 

通知虛假處理恰好一半的相關案件。這是值得咀嚼上述。

爲了實現is_same,我們在編譯時需要一個true或false的概念。我們可以通過靜態const變量來保證這一點。我們希望is_same只要其模板參數相同,就具有編譯時真值。專業化體系規則直接處理這個問題。

template<typename, typename> 
struct is_same{ 
    static const bool value = false; 
}; 

template<typename T> 
struct is_same<T, T>{ 
    static const bool value = true; 
}; 

這應該做你想做的。請注意,您可以進一步抽象並製作另一對結構。

struct false_type { 
    static const bool value = false; 
}; 

struct true_type { 
    static const bool value = true; 
}; 

然後is_same變爲:

template<typename, typename> 
struct is_same : false_type {}; 

template<typename T> 
struct is_same<T, T> : true_type {}; 

這使得它看起來更像是一個功能。

我更喜歡這個類別的解決方案,因爲它更容易將元程序分離成頭文件。然後你可以在其他地方重新使用邏輯儘管如此,如果你不使用C++ 11甚至不使用boost,那麼編譯必要的編譯時間函數可能會讓人頭疼。

如果使用複雜(或任何簡單的重新設計)滿足您當前和未來的要求 - 更喜歡這一點。否則我認爲這個解決方案是合理的未來證明。

+0

很好的答案和解釋!我試了一下它的工作完美無缺(至少對於我的小測試程序:)。非常感謝! – MetroWind

0

這可能幫助。

#include<utility> 

template<class Type> 
struct test{ 
private: 
    struct _matrix{}; 
    struct _scalar{}; 
    template<class T> 
    struct category{ 
     typedef _scalar type; 
    }; 
    template<class T> 
    struct category<test<T>>{ 
     typedef _matrix type; 
    }; 

    template<class T> 
    void do_foo(T, _matrix){} 

    template<class T> 
    void do_foo(T, _scalar){} 

public: 
    //c++11 
    template<class T> 
    void operator*(T&& a){ 
     do_foo(std::forward<T>(a), category<T>::type()); 
    } 
    //Older Compiler 
    //template<class T> 
    //void operator*(const T& a){ 
    // do_foo(a, category<T>::type()); 
    //} 

}; 

int main(){ 
    typedef test<int> int_matrix; 
    int_matrix obj; 
    obj*int_matrix(); 
    obj*obj; 
    obj*1; 
    obj*1.; 

    return 0; 
} 
+0

您能否提供一些有關此代碼如何工作的提示?此外,我可能無法訪問C++ 11編譯器。我需要在一些具有奇怪的舊編譯器的超級計算機上運行此代碼... – MetroWind

+0

使用的功能是函數重載(do_foo)和類模板特化。 std :: forward不是必需的。所以這個技巧將會在較老的編譯器上生效。您的原始代碼不起作用的原因是Matrix 沒有首選匹配項。事實上,我認爲在您的案例中似乎不可能與兩個函數模板進行首選匹配,我能想到的唯一解決方案是函數重載。 PS:我認爲你可以爲類矩陣和T定義運算符。由於自動類型轉換,不需要所有類型。 – cqdjyy01234

+0

我認爲標量乘法不應該是模板,因爲自動類型轉換。 – cqdjyy01234