如何避免C ++类模板中的无限递归

时间:2009-08-09 20:00:50

标签: c++ templates recursion matrix

我有一个矩阵类,其大小由模板参数决定。

template <unsigned cRows, unsigned cCols>
class Matrix {
    ...
};

我的程序使用几种尺寸的矩阵,通常为2x2,3x3和4x4。通过使用模板参数而不是运行时参数设置矩阵大小,编译器可以进行大量的内联和优化。

但是现在我需要一个成员函数来返回一个新的矩阵,该矩阵的行数减少一个,列数减少一个。

Matrix<cRows - 1, cCols - 1> Reduced(unsigned row, unsigned col) const { ... }

这个想法是它将返回一个删除了指定行和列的矩阵。在实践中,只会使用至少包含三行和三列的矩阵调用此方法,并在最小的位置返回2x2。

编译器没有看到下限,因此它在无限递归中陷入困境,试图以不断减小的大小实例化模板。我尝试在函数本身中放入两条线索,这些较小的尺寸不会出现:

Matrix<cRows - 1, cCols - 1> Reduced(unsigned row, unsigned col) const {
    static_assert(cRows > 1 && cCols > 1);
    if (cRows <= 1 || cCols <= 1) throw std::domain_error();
    Matrix<cRows - 1, cCols - 1> r;
    // ... initialize r ...
    return r;
}

static_assertif - 语句似乎都不足以让编译器永远不会生成0x0矩阵。 (具有讽刺意味的是,它确实抱怨if - 语句具有恒定的编译时条件。)

有没有人对如何避免这种编译时无限递归有任何建议?

5 个答案:

答案 0 :(得分:12)

您需要为没有行或没有列的Matrix提供专门化。

E.g。

template<unsigned cRows>
class Matrix< cRows, 0 >
{
    Matrix<cRows - 1, 0> Reduced() { return Matrix<cRows - 1, 0>(); }
};


template<unsigned cCols>
class Matrix< 0, cCols >
{
    Matrix<0, cCols - 1> Reduced() { return Matrix<0, cCols - 1>(); }
};


template<>
class Matrix< 0, 0 >
{
    Matrix<0, 0> Reduced() { return Matrix<0, 0>(); }
};

您遇到的问题是尝试使用一组特定的模板参数实例化Matrix Reduced函数时,总是需要为不同的参数集(cRows - 1,cCols -1)实例化Matrix模板。这种递归必须在某处停止。如果你只处理方形矩阵,那么你可以减少专业化。

此外,如果您永远不会使用1x1矩阵,那么您可以使用完全为空的类来停止递归,这是2x2矩阵的reduce结果。

template<>
class Matrix< 1, 1 > {};

答案 1 :(得分:1)

您可以为不包含该方法的小值cRows或cCol指定模板特化。

答案 2 :(得分:1)

您似乎对编译时间和运行时行为感到困惑,我对您的代码感到有点困惑,但我认为您想要的是值0,0的模板的特化,它终止了递归。

如果您还没有,我建议您阅读 Vandervoorde&amp ;; C++ Templates: The Complete Guide Josuttis,详细介绍了这类事情。

答案 3 :(得分:0)

您需要明确指定希望递归结束的情况的行为。有关详细信息,请参阅this DDJ article。以下是文章中的一个简单示例:

template<int n>
class META_FACTORIAL
{
public:
  enum{
    RET = n * META_FACTORIAL<n-1>::RET
  };
};

template<>
class META_FACTORIAL<0>
{
public:
  enum{ RET = 1 };
};

答案 4 :(得分:0)

不是专门化整个类来终止递归,另一个选择可能是在函数上使用boost::enable_if,使其仅在矩阵大小超过2x2时可用。