避免在模板专业中避免代码重复

Avoiding code duplication in template specializations

本文关键字:代码      更新时间:2023-10-16

我有一个矩阵类

template<typename Type, size_t Rows, size_t Columns> class Matrix
{
public:
    Type Entries[Rows][Columns];
    // Operators, etc.
};

我需要对此类的专业(用于平方矩阵等(进行专业化,这意味着我需要为每个专业化重写整个类实现。然后,我开始考虑避免代码重复的方法。

想到的第一个想法是定义基本模板中的专业功能,但将它们删除,以便在基本模板中使用它们并在类外定义专业。

// Inside class definition
auto GetDeterminant() const -> T = delete;
// Outside class definition
template<typename Type, size_t Size> Matrix<T, Size, Size>::GetDeterminant() const -> T
{
    // Implementation
}

但是,在遇到一些错误之后,我了解到不允许部分专门化功能模板。所以我尝试了这些方法

方法1:

template<typename Derived, typename Type, size_t Rows, size_t Columns> class MatrixBase
{
public:
    T Entries[Row][Columns];
    // Operators, etc.
}
template<typename Type, size_t Rows, size_t Columns> class Matrix : public MatrixBase<Matrix<Type, Rows, Columns>, Rows, Columns>
{
    // General MxN matrix stuff
}
template<typename Type, size_t Size> class Matrix<T, Size, Size> : public MatrixBase<Matrix<T, Size, Size>, Size, Size>
{
public:
    // Specific MxM matrix stuff
}

在这里,我在另一个问题上实现了 crtp ,但是我觉得抽象不需要,尽管可能有更好的方法。所以我想到了另一种方法。

方法2:

template<typename T, size_t Rows, size_t Columns> class Matrix
{
public:
    T Entries[Rows][Columns]
    auto GetDeterminant() const -> T
    {
        static_assert(Rows == Columns);
        // Some computations
    }
}

这似乎很好,如果有人试图在非平方矩阵上使用它,则会遇到编译时间错误。

问题:

有没有使用模板来解决此问题的方法?还是我应该坚持其中一种方法?

i使用sfinae提出了一种方法2的变体来启用或禁用GetDeterminant()

template <typename Type, size_t Rows, size_t Columns>
class Matrix
 {
   public:
      Type Entries[Rows][Columns];
      template <size_t R = Rows>
      typename std::enable_if<R == Columns, Type>::type GetDeterminant () const
       {
         // some computations
         return {};
       }
 };

如果要避免GetDeterminant()可以验证模板R值的风险,如下所示

Matrix<int,  3, 3>  m0;
Matrix<long, 3, 2>  m1;
auto d0 = m0.GetDeterminant(); // compile
//auto d1 = m1.GetDeterminant(); // compilation error
auto d1 = m1.GetDeterminant<2>(); // compile!!!

您可以维护static_assert(例如static_assert(R == Rows, "!");(,也可以如下改进std::enable_if测试

  template <size_t R = Rows>
  typename std::enable_if<(R == Columns) && (R == Rows), Type>::type
      GetDeterminant () const
   {
     // some computations
     return {};
   }

首先,引用Aschelper的评论:

我喜欢static_assert方法。

我也是。很明显,简洁,可以进一步给出有意义的解释错误消息。


助手struct(在实现名称空间中(

namespace matrix_impl {
  template<std::size_t Rows, std::size_t Columns>
  struct determinate_calculation {
    int operator()(int const (& entries)[Rows][Columns]) const = delete;
  };
  // or just don't define the class template
  template<std::size_t Size>
  struct determinate_calculation<Size, Size> {
    int operator()(int const (& entries)[Size][Size]) const {
      return entries[0][0]; // Woah, calculation wrong as hell ;)
    }
  };
}
template<std::size_t Rows, std::size_t Columns>
struct Matrix {
  int entries[Rows][Columns];
  int get_determinate() const {
    return matrix_impl::determinate_calculation<Rows, Columns>{}(entries);
  }
};
  • (-1(需要类模板定义,包括删除函数或类模板声明。这只是样板。
  • (-1(需要一个类模板专业化,其中包含某些函数中的实际代码。不仅仅是编写一个功能。
  • (-1/1(需要将Matrix类的所需成员作为参数传递给该功能。很好,因为它使依赖关系明确。不好,因为有很多成员可能会变成麻烦("嘿,让我们使用一个类参数传递",只是添加到样板上(。

std::enable_if

max66更快,不再需要添加。