模板和铸件

Templates and casting

本文关键字:      更新时间:2023-10-16

我用模板创建了一个矩阵类:

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;
}

[注意:两个参数都是按值传递的,第二个参数是因为对于算术类型,按值传递更高效、更惯用;第一个参数是由于通过将副本移动到函数的接口,编译器可以消除副本]。这种方法很简单,但对于程序中ST的每个组合,都会生成一个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,而是当前的实例化类型(intdouble…)

该语言允许在具有声明的类中定义friend函数,并将在命名空间级别定义函数,尽管声明只能通过Argument Dependent Lookup找到。

无论何时实例化模板的特定实例(比如Matrix<int>)并调用运算符,编译器都会为您生成免费函数。因为该函数没有模板化,所以它将允许对参数进行转换,因此对于Matrix<int> m,它将允许您通过将5.转换为int来调用m * 5.

相关文章:
  • 没有找到相关文章