模板和铸件
Templates and casting
我用模板创建了一个矩阵类:
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)
我该如何解决这种行为?是否可以让doubles与其他算术类型相乘?
当然,只需为模板化的自由函数和成员函数允许两个参数。
例如:
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
未定义,这将触发编译时错误。
是。将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;
}
您看到的问题是,模板类型推导需要所有推导类型的完美匹配。在您的例子中,您有一个模板,它接受一个类型参数T
,它既是标量类型又是矩阵类型。当编译器看到操作5 * m1
时,它会为第一个参数推导T == int
,但为第二个参数推导出T == double
,类型推导失败。
有多种方法可以解决这个问题,正如建议的那样,您可以添加第二个模板参数:
template <typename T, typename S>
Matrix<T> operator*( Matrix<T> m, S scalar ) {
return m*=scalar;
}
[注意:两个参数都是按值传递的,第二个参数是因为对于算术类型,按值传递更高效、更惯用;第一个参数是由于通过将副本移动到函数的接口,编译器可以消除副本]。这种方法很简单,但对于程序中S
和T
的每个组合,都会生成一个operator*
,即使operator*=
中的实际乘法总是在T
上执行。
另一种方法是固定要乘以的scalar
的类型,例如,将其设为double
,每个相乘的T
类型只生成一个operator*
:
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
函数,并将在命名空间级别定义函数,尽管声明只能通过Argument Dependent Lookup找到。
无论何时实例化模板的特定实例(比如Matrix<int>
)并调用运算符,编译器都会为您生成免费函数。因为该函数没有模板化,所以它将允许对参数进行转换,因此对于Matrix<int> m
,它将允许您通过将5.
转换为int
来调用m * 5.
。
- 没有找到相关文章