gpt4 book ai didi

c++ - 为什么重载运算符时会出现编译错误而不是替换失败?

转载 作者:行者123 更新时间:2023-11-30 04:51:19 24 4
gpt4 key购买 nike

我正在编写 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 给了我一堆与此类似的错误:

error: type 'int' cannot be used prior to '::' because it has no members static_assert(Matrix1::number_of_cols == Matrix2::number_of_rows);

在我看来,这应该是替换失败而不是编译错误。给了什么?

最佳答案

从这段代码来看:

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) {...}

如果第一个模板参数为假,您似乎希望完全丢弃第二个模板参数(无需任何验证)。

但这里发生的是,两个模板参数都先被验证,然后如果条件结果为假,则第二个参数被丢弃。

自从使用 Matrix_product_type使用非矩阵模板参数是一个硬错误(而不是 SFINAE 可检测的错误),你在 enable_if_t 之前得到一个硬错误有机会上场。


最直接的解决方案是为 enable_if_t 创建第二个模板参数无论条件是否为真都有效。

一种可能的方法是:

template <typename Matrix1, typename Matrix2>
typename std::enable_if_t<(Is_matrix<Matrix1> && Is_matrix<Matrix2>),
matrix_product_type<Matrix1, Matrix2>>::type
operator*(const Matrix1 &A, const Matrix2 &B)

请注意,我替换了 Matrix_product_type (类型别名)与 matrix_product_type (struct)。

这边matrix_product_type<>::type除非条件为真,否则不会被访问。

您可能需要对 operator* 的其他重载进行类似的更改

关于c++ - 为什么重载运算符时会出现编译错误而不是替换失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54847294/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com