【发布时间】:2019-02-23 23:29:32
【问题描述】:
我正在编写 Matrix 类层次结构,但是在重载乘法运算符以将矩阵与矩阵相乘以及通过标量缩放矩阵时遇到麻烦。我使用 std::enable_if_t 来区分应该调用哪个运算符,具体取决于要相乘的类型是否为矩阵。一个变量是否为矩阵是通过检查它是否继承自一个空的基类matrix_tag来确定的。 下面是一个相当少的可重现代码示例:
#include <type_traits>
#include <vector>
// Forward declaration
template <typename T, size_t N, size_t M> class Matrix;
// Empty base class for all matrices, to enable checking whether a type is a
// matrix
struct matrix_tag {};
template <typename MatrixType> struct is_matrix {
static constexpr bool value =
std::is_base_of<matrix_tag, MatrixType>::value;
};
template <typename MatrixType>
constexpr bool Is_matrix = is_matrix<MatrixType>::value;
// Helper type function: The result of multiplying two generic types
template <typename T1, typename T2> struct product_type {
using type = decltype(std::declval<T1>() * std::declval<T2>());
};
// Convenience wrapper
template <typename T1, typename T2>
using Product_type = typename product_type<T1, T2>::type;
// Compile time dispatch for the result of matrix multiplications
template <typename Matrix1, typename Matrix2> struct matrix_product_type {
static constexpr size_t N = Matrix1::number_of_rows;
static constexpr size_t M = Matrix2::number_of_cols;
static_assert(Matrix1::number_of_cols == Matrix2::number_of_rows);
using element_type = Product_type<typename Matrix1::element_type,
typename Matrix2::element_type>;
using type = Matrix<element_type, N, M>;
};
// Convenience wrapper
template <typename Matrix1, typename Matrix2>
using Matrix_product_type =
typename matrix_product_type<Matrix1, Matrix2>::type;
// Compile time dispatch for Matrix scaling
template <typename MatrixType, typename T> struct scaled_matrix_type {
static constexpr size_t N = MatrixType::number_of_rows;
static constexpr size_t M = MatrixType::number_of_cols;
using element_type = Product_type<typename MatrixType::element_type, T>;
using type = Matrix<element_type, N, M>;
};
// Convenience wrapper
template <typename MatrixType, typename T>
using Scaled_matrix_type = typename scaled_matrix_type<MatrixType, T>::type;
// Class definition
template <typename T, size_t N, size_t M> class Matrix : public matrix_tag {
public:
// Types
using element_type = T;
// Traits
static constexpr size_t number_of_rows = N;
static constexpr size_t number_of_cols = M;
// Default constructor
Matrix() : elements_(N * M, 0) {}
// Public access functions
virtual const T &operator()(size_t row, size_t col) const {
return elements_[row * number_of_cols + col];
}
virtual T &operator()(size_t row, size_t col) {
return const_cast<T &>(
(*static_cast<const Matrix *>(this))(row, col));
}
private:
// Element storage
std::vector<T> elements_;
};
// Scaling
// Returns a new matrix with element_type reflecting the result of
// elementwise multiplication
template <typename MatrixType, typename T>
std::enable_if_t<(Is_matrix<MatrixType> && !Is_matrix<T>),
Scaled_matrix_type<MatrixType, T>>
operator*(const MatrixType &A, const T &x) {
typename scaled_matrix_type<MatrixType, T>::type B = A;
for (size_t i = 0; i != B.number_of_rows; ++i) {
for (size_t j = 0; j != B.number_of_rows; ++j) {
B(i, j) *= x;
}
}
return B;
}
template <typename T, typename MatrixType>
std::enable_if_t<(!Is_matrix<T> && Is_matrix<MatrixType>),
Scaled_matrix_type<MatrixType, T>>
operator*(const T &x, const MatrixType &A) {
return A * x;
}
// Matrix multiplication
template <typename Matrix1, typename Matrix2>
std::enable_if_t<(Is_matrix<Matrix1> && Is_matrix<Matrix2>),
Matrix_product_type<Matrix1, Matrix2>>
operator*(const Matrix1 &A, const Matrix2 &B) {
typename matrix_product_type<Matrix1, Matrix2>::type C;
for (size_t i = 0; i != A.number_of_rows; ++i) {
for (size_t j = 0; j != B.number_of_cols; ++j) {
for (size_t k = 0; k != A.number_of_cols; ++k) {
C(i, j) += A(i, k) * B(k, j);
}
}
}
return C;
}
int main() {
Matrix<double, 4, 3> A{};
Matrix<float, 3, 2> B{};
auto C = A * B;
}
Clang 给了我一堆类似于这个的错误:
错误:类型 'int' 不能在 '::' 之前使用,因为它没有 会员 static_assert(Matrix1::number_of_cols == Matrix2::number_of_rows);
在我看来,这应该是替换失败而不是编译错误。什么给了?
【问题讨论】:
-
你想让实例化更懒惰。即让 enable_if_t 为您提供 scaled_matrix_type,并且只能访问 enable_if 之外的 ::type。这样编译器就不需要实例化 scaled_matrix_type 除非 enable_if 中的条件为真。
标签: c++ c++11 templates template-meta-programming sfinae