模板和铸造

时间:2012-08-18 12:26:47

标签: c++ templates

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

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)

我该如何解决这个问题呢?可以将双精度乘以其他算术类型吗?

3 个答案:

答案 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(不带参数)来引用当前实例化(MatrixMatrix<int> ...) [这可能看起来不太重要,但是,当Matrix<double引用模板时,以及当它引用从模板生成的类时,重要的是要意识到这一点。该函数的第二个参数是Matrix。同样,这不是类模板的通用T,而是当前实例化类型(Tint ...)。

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

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