4

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

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)

我该如何解决这种行为?可以让双精度数乘以其他算术类型吗?

4

3 回答 3

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

如果您尝试将 Matrix 与无意义的东西相乘,即如果T*U未定义,这将触发编译时错误。

于 2012-08-18T12:28:52.047 回答
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;
}
于 2012-08-18T12:32:06.693 回答
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*的每个组合生成一个,即使实际的乘法总是在 上执行。SToperator*=T

另一种方法是修复scalar要相乘的类型,例如, make it ,每种相乘的类型double仅生成一个:operator*T

template <typename T>
Matrix<T> operator*( Matrix<T> m, double scalar ) {
   return m*=scalar;
}

在这种方法中,只有一个operator*,将 adouble作为参数。与前面的示例一样,它可能需要对标量进行两次类型转换(假设您乘以Matrix<int>5然后它将转换5为 a 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<doubleMatrix当它引用从模板生成的类时]。函数的第二个参数是T。同样,这不是T类模板的泛型,而是当前的实例化类型 ( int, double...)。

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

每当您实例化模板的特定实例(例如Matrix<int>)并调用运算符时,编译器都会为您生成自由函数。因为该函数不是模板化的,所以它允许对参数进行转换,因此Matrix<int> m它允许您m * 5.通过转换5.int.

于 2012-08-18T21:13:00.623 回答