我正在写一个矩阵模板类。一切顺利,直到我重载乘法运算符。我的班级看起来像这样:
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;
}
答案 0 :(得分:3)
我将用c ++ 11描述一个解决方案,然后解释如何在c ++ 98中实现它。
在c ++ 11中,标题<type_traits>
包括类型函数和类型谓词。这使得强制约束更加方便。
std::is_same<T1, T2>::value
与T1
的类型相同,则 T2
为真,否则为false。
typename std::enable_if< bool, T >::type
是一个定义良好的类型T
,如果bool是真的并且定义不明确。
当编译器查找候选模板函数和方法时,如果尝试的特化失败,则不会抛出错误。它只是抛弃那个候选人。这意味着以下代码将消除歧义:
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> {};
通知false正好处理一半相关案件。咀嚼上述内容是值得的。
为了实现is_same
,我们在编译时需要一个真或假的概念。我们可以使用静态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,那么制作必要的编译时功能可能会令人头疼。
如果使用复杂(或任何简单的重新设计)满足您当前和未来的要求 - 更喜欢。否则我认为这个解决方案是合理的未来证据。
答案 1 :(得分: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;
}