2013-10-16 87 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