【问题标题】:How can I minimize repeated template type names?如何最大程度地减少重复的模板类型名称?
【发布时间】:2023-02-11 03:56:44
【问题描述】:

这个 sn-p 是 C++20 代码库中的一个小例子。将矩阵相互相乘是一项免费功能。 Matrix 本身是在 ROWS 和 COLUMNS 上模板化的,就像 std::array 一样,这使得在函数接口中使用有点痛苦。

完整的类型名称变成了Matrix<ROWS, COLUMNS>,这本身很好,但是当重复三次(对于返回值和两个参数)时,它确实会损害函数接口的可读性。

现代 C++ 中有哪些语法或策略可以减少重复类型名称的干扰?

template <uint8_t ROWS, uint8_t COLUMNS>
Matrix<ROWS, COLUMNS> operator*(const Matrix<ROWS, COLUMNS>& lhs, const Matrix<ROWS, COLUMNS>& rhs) noexcept {
    Matrix<ROWS, COLUMNS> result;
    for (uint8_t row = 0; row < ROWS; ++row) {
        for (uint8_t col = 0; col < COLUMNS; ++col) {
            for (uint8_t i = 0; i < COLUMNS; ++i) {
                result(row, col) += lhs(row, i) * rhs(i, col);
            }
        }
    }
    return result;
}

要求:

  • 所有矩阵(参数和返回值)必须具有相同的维度
  • 矩阵知道自己的大小(.columns().rows()),因此我们不需要在这些循环中使用模板参数。
  • Matrix 还提供了一个::size_type,所以理想的解决方案是让我们使用它(干干净净) 而不是在循环中硬编码uint8_t

【问题讨论】:

  • auto operator*(const Matrix&lt;ROWS, COLUMNS&gt;&amp; lhs, decltype(lhs) rhs)
  • 定义一个 matrix 概念,然后是类似 template &lt;matrix LHS, std::same_as&lt;LHS&gt; RHS&gt; LHS operator*(const LHS&amp; lhs, const RHS&amp; rhs) 的概念。
  • ^That,并让维度成为静态成员。然后你写for(LHS::size_type row = 0; row &lt; lhs.rows; row++) ...。此外,使用 uint8_t 作为索引听起来像是等待发生的溢出。
  • 你可以让它成为朋友并在类模板中定义它
  • 最简单的,如果你在类本身内部实现它,因为在这种情况下你可以简单地省略模板参数。您甚至不需要为此使用特定的 C++ 标准。 template &lt;uint8_t ROWS, uint8_t COLUMNS&gt; class Matrix{... friend Matrix operator*(Matrix const&amp; lhs, Matrix const&amp; rhs) { ... } };,请参阅此处的 (2):en.cppreference.com/w/cpp/language/friend

标签: c++ api templates coding-style


【解决方案1】:

一种替代方法是使用 auto 返回类型,并将模板参数更改为完整类型。是这样的:

template <class Matrix>
constexpr auto operator*(const Matrix& lhs, const Matrix& rhs) noexcept {
    using size_type = Matrix::size_type;
    Matrix result;
    for (size_type row = 0; row < Matrix::HEIGHT; ++row) {
        for (size_type col = 0; col < Matrix::WIDTH; ++col) {
            for (size_type i = 0; i < Matrix::HEIGHT; ++i) {
                result(row, col) += lhs(row, i) * rhs(i, col);
            }
        }
    }
    return result;
}

这当然更具可读性,我相信它仍然会强制两个参数必须是相同的类型/大小。

这样做的一个缺点可能是模板变得过于随意。它也可以为非矩阵类型实例化(并失败)。有没有一种简单的方法(概念?)将模板限制为类似矩阵的类?

【讨论】:

  • 这当然是可能的。在此处尝试一些解决方案 stackoverflow.com/questions/54182239 并更新您的答案以使用您认为适合此用例的解决方案。
  • 这只适用于方阵
【解决方案2】:

现代 C++ 中有哪些语法或策略可以减少重复类型名称的干扰?

实现此目的的最简单方法是使 operator* 成为 friend 函数,并在类本身内部完成实现。

然而,如果你坚持在课堂之外,可以通过一个abbreviated function template(自)。

#include <type_traits> // std::decay_t

auto operator*(const auto& lhs, const auto& rhs) noexcept 
{
    // static assert for type check!
    static_assert(std::is_same_v<std::decay_t<decltype(lhs)>
                               , std::decay_t<decltype(rhs)>>, "are not same type!");

    std::decay_t<decltype(lhs)> result;
    for (auto row = 0u; row < lhs.rows(); ++row) {
        for (auto col = 0u; col < lhs.columns(); ++col) {
            for (auto i = 0u; i < lhs.columns(); ++i) {
                // calculate result
            }
        }
    }
    return result;
}

也就是说,现在 operator* 接受任何类型相同的参数。但是,我们只需要 Matrix&lt;ROWS, COLUMNS&gt; 类型。检查、传递类型的类型特征是 Matrix 类型,在这里有帮助。

#include <type_traits> // std::false_type, std::true_type

template <typename> struct is_Matrix final : std::false_type{};
template <std::size_t ROWS, std::size_t COLUMNS>
struct is_Matrix<Matrix<ROWS, COLUMNS>> final : std::true_type {};

// or variable template
// template <typename> inline constexpr bool is_Matrix = false;
// template <std::size_t ROWS, std::size_t COLUMNS>
// inline constexpr bool is_Matrix<Matrix<ROWS, COLUMNS>> = true;

现在使用该特征,您可以限制(即SFINAE或者static_assert或者concepting) operator* 仅用于 Matrix 类型。

See live example code

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-02
    • 2018-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多