我试图实现向量和矩阵表达式模板。两者都有运算符+重载,但我得到模棱两可的运算符错误。
如何在保留表达式模板效果的同时重载矩阵和向量的运算符?
这是我的矩阵和模板:
template<typename E1, typename E2>
class MatSum: public MatExpression<MatSum<E1,E2>>{
E1 const& _u;
E2 const& _v;
public:
MatSum(E1 const &u, E2 const &v): _u(u), _v(v){
assert(u.height() == v.height() && u.width() == v.width());
}
double operator[](size_t i) const {return _u[i] + _v[i]; }
size_t size() const {return _u.size();}
size_t width() const {return _u.width();}
size_t height() const {return _u.height();}
};
template<typename E1, typename E2>
MatSum<E1,E2> operator+(E1 const& u, E2 const& v){
return MatSum<E1,E2>(u,v);
}
这是矢量和模板:
template <typename E1, typename E2>
class VecSum : public VecExpression<VecSum<E1, E2>>{
E1 const& _u;
E2 const& _v;
public:
VecSum(E1 const & u, E2 const &v): _u(u), _v(v){
assert(u.size() == v.size());
}
double operator[](size_t i) const {return _u[i] + _v[i]; }
size_t size() const {return _v.size(); }
};
template <typename E1, typename E2>
VecSum<E1, E2> operator+(E1 const &u, E2 const &v){
return VecSum<E1,E2>(u,v);
}
以及导致错误的代码片段:
Vec v1 = {67.12,34.8,90.34};
Vec v2 = {34.90,111.9,45.12};
Vec sum = v1+v2;
Matrix m0 = {{1.0,1.0,1.0},{1.0,1.0,1.0},{1.0,1.0,1.0}};
Matrix m1 = {{1.0,1.0,1.0},{1.0,1.0,1.0},{1.0,1.0,1.0}};
Matrix summ = m0 + m0;
模板不能专门用于返回类型。
在您的情况下,编译器无法选择要调用的operator+
,因为它们都可以使用任何两种类型实例化。请考虑以下示例:
Vec v1 = {67.12,34.8,90.34};
Matrix m1 = {{1.0,1.0,1.0},{1.0,1.0,1.0},{1.0,1.0,1.0}};
auto sum = v1+m1;
这里应该调用两个运算符中的哪一个?对于编译器来说,这是模棱两可的。
您可以创建两个非模板运算符+,它们具有(Vec, Vec)
和(Matrix, Matrix)
参数,或者使用 SFINAE 检查模板运算符参数类型是否存在某些条件,并替换所需的专用化operator+
。
您的两个模板之间没有什么可以区分的,所以这是一个模棱两可的调用。您必须将返回 MatSum
的参数限制为仅适用于类似矩阵的参数,和/或返回 VecSum
的参数仅适用于类似矢量的参数。
假设您将成员别名using kind = Mat;
添加到MatExpression
和Matrix
,using kind = Vec
添加到VecExpression
和Vector
。然后我们创建一个特征类
template<typename E1, typename E2, typename = typename E1::kind, typename = typename E2::kind>
struct Traits;
template<typename E1, typename E2>
struct Traits<E1, E2, Mat, Mat>
{
using sum_type = MatSum<E1, E2>;
// others, e.g. using prod_type = MatProd<E1, E2>;
};
template<typename E1, typename E2>
struct Traits<E1, E2, Vec, Vec>
{
using sum_type = VecSum<E1, E2>;
// others, e.g. using prod_type = VecProd<E1, E2>;
};
和一些别名模板,例如
template<typename E1, typename E2>
using sum_t = typename Traits<E1, E2>::sum_type;
然后,我们可以将+
重载限制为仅在Traits
存在时才存在。
template<typename E1, typename E2>
sum_t<E1, E2> operator+(E1 const& u, E2 const& v){
return sum_t<E1, E2>(u, v);
}