避免模​​板特化中的代码重复

时间:2017-04-17 00:58:20

标签: c++ c++11 templates template-specialization

我有一个Matrix类

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 ,就像在另一个question上看到的那样,但是我觉得抽象有点不需要,尽管可能有更好的方法。所以我提出了另一种方法。

方法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
    }
}

这似乎很好,如果有人试图在非方阵上使用它,就会出现编译时错误。

问题:

有没有办法只使用模板来解决这个问题?或者我应该坚持使用其中一种方法?

2 个答案:

答案 0 :(得分:0)

我建议使用SFINAE启用或禁用GetDeterminant()

的方法2的变体
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 {};
   }

答案 1 :(得分:0)

首先,引用aschelper的评论:

  

我喜欢static_assert方法。

我也是。它清晰,简洁,可以给出有意义的解释错误信息。

helper 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 was faster,无需再添加。