簡體   English   中英

C ++矩陣模板,矩陣矩陣與矩陣數乘法之間的歧義

[英]C++ Matrix template, ambiguity between matrix-matrix and matrix-number multiplication

我正在寫一個矩陣模板類。 一切順利,直到我使乘法運算符超載為止。 我的課看起來像這樣:

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

我希望使用相同的*運算符覆蓋矩陣和數字之間所有可能的乘法組合。

然后,我編寫了一個小的測試程序,將兩個Matrix<double>相乘,我的clang ++編譯器抱怨模棱兩可:

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

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

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

如果T1T2類型相同std::is_same<T1, T2>::value為true,否則為false。

如果bool為true,則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<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具有編譯時為true的值。 專業化系統規則直接處理此問題。

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,那么進行必要的編譯時函數可能會令人頭疼。

如果使用復雜的(或任何簡單的重新設計)可以滿足您當前和將來的要求,請首選。 否則,我認為該解決方案可以合理地面向未來。

這可能會有所幫助。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM