我用模板创建了一个矩阵类:
template <typename T>
class Matrix
{
static_assert(std::is_arithmetic<T>::value,"");
public:
Matrix(size_t n_rows, size_t n_cols);
Matrix(size_t n_rows, size_t n_cols, const T& value);
// Functions
// Operators
Matrix<T>& operator*=(const T& value)
private:
size_t rows;
size_t cols;
std::vector<T> data;
};
我创建了以下两个(外部)运算符,将我的矩阵与一个数字相乘:
// Inner operator used by the externals ones
template <typename T>
inline Matrix<T>& Matrix<T>::operator*=(const T& value)
{
for(size_t i(0); i < data.size(); i++)
{
data[i] *= value;
}
return *this;
}
template <typename T>
inline Matrix<T> operator*(const T& value, const Matrix<T>& matrix)
{
Matrix<T> tmp(matrix);
return tmp *= value;
}
template <typename T>
inline Matrix<T> operator*(const Matrix<T>& matrix, const T& value)
{
return value * matrix;
}
问题在于,如果我将矩阵声明为双精度,我可以将矩阵乘以双精度等等......
Matrix<double> m1(3,3,1.);
5. * m1; // Works
5 * m1; // Doesn't work (5 is an int and not a double)
我该如何解决这个问题呢?可以将双精度乘以其他算术类型吗?
答案 0 :(得分:4)
当然,只需要为模板化的自由函数和成员函数提供两个参数。
例如:
template <typename T> class Matrix {
/* ... */
template <typename U>
inline Matrix<T>& operator*=(const U& value)
{
for(size_t i(0); i < data.size(); i++)
{
data[i] *= value;
}
return *this;
}
};
template <typename T, typename U>
inline Matrix<T> operator*(const U& value, const Matrix<T>& matrix)
{
Matrix<T> tmp(matrix);
return tmp *= value;
}
如果你试图将你的矩阵与无意义的东西相乘,也就是说,如果未定义T*U
,这将触发编译时错误。
答案 1 :(得分:1)
是。将Matrix类中的函数声明为
template <typename T>
class Matrix
{
public:
/* ... */
template <typename S>
inline Matrix & operator*=( const S & value );
/* ... */
};
该定义类似于
template <typename T>
template <typename S>
inline Matrix<T>& Matrix<T>::operator*=(const S& value)
{
for(size_t i(0); i < data.size(); i++)
{
data[i] *= value;
}
return *this;
}
为成员函数。您需要两次写template
。有点奇怪,但这是C ++语法。
如果是免费功能,您可以写
template <typename T, typename S>
inline Matrix<T> operator*(const S& value, const Matrix<T> &mat)
{
Matrix<T> tmp(mat);
return tmp *= value;
}
答案 2 :(得分:0)
您看到的问题是模板类型推导需要所有推导类型的完美匹配。在您的情况下,您有一个模板,它采用单个类型参数T
,它既是标量类型,也是矩阵类型。当编译器看到操作:5 * m1
时,它会为第一个参数推导出T == int
,但为第二个参数推导出T == double
,并且类型推导失败。
围绕此方法有多种方法,如建议的那样,您可以添加第二个模板参数:
template <typename T, typename S>
Matrix<T> operator*( Matrix<T> m, S scalar ) {
return m*=scalar;
}
[注意:两个参数都是按值计算的,第二个是因为对于算术类型来说,它更有效且惯用于传递值;第一个是因为通过将副本移动到函数的接口,允许编译器忽略副本]。这种方法很简单,但会为程序中operator*
和S
的每个组合生成一个T
,即使operator*=
中的实际乘法始终在{{{}}上执行1}}。
另一种方法是修复您想要乘以的T
类型,例如,将其设为scalar
,每double
只生成一个operator*
乘以的类型:
T
在这种方法中,有一个template <typename T>
Matrix<T> operator*( Matrix<T> m, double scalar ) {
return m*=scalar;
}
,一个以operator*
为参数。与前面的示例一样,它可能需要在标量上进行两次类型转换(例如,您将double
乘以Matrix<int>
,然后它会将5
转换为5
,这将然后转换回double
以匹配int
的签名。
第三种方法是创建一个非模板化函数,它使您的operator*=
和另一个相同类型的参数。这将是最接近原始代码的,具有不作为模板的轻微优势,它将允许标量参数的转换。从理论上讲,您可以手动定义所有这些功能:
Matrix
但这很容易成为维护问题。幸运的是,该语言中有一个功能允许定义所有非模板函数一般。虽然语法可能不是最自然的。您只需要声明将自由函数作为模板的朋友,然后在中定义 类模板定义:
Matrix<int> operator*( Matrix<int>, int ) { ... }
Matrix<double> operator*( Matrix<double>, double ) { ... }
由于我们在类模板template <typename T>
class Matrix {
// ...
friend Matrix operator*( Matrix m, T scalar ) { return m*=scalar; }
};
中,我们可以使用Matrix
(不带参数)来引用当前实例化(Matrix
,Matrix<int>
...) [这可能看起来不太重要,但是,当Matrix<double
引用模板时,以及当它引用从模板生成的类时,重要的是要意识到这一点。该函数的第二个参数是Matrix
。同样,这不是类模板的通用T
,而是当前实例化类型(T
,int
...)。
该语言允许在具有声明的类中定义double
函数,并且在命名空间级别定义函数,尽管声明只能通过参数依赖查找。
每当您实例化模板的特定实例(例如friend
)并调用运算符时,编译器将为您生成自由函数。由于该函数不是模板化的,因此它允许对参数进行转换,因此对于Matrix<int>
,它允许您通过将Matrix<int> m
转换为m * 5.
来调用5.
。 / p>