【问题标题】:How to avoid infinite recursion in C++ class templates如何避免 C++ 类模板中的无限递归
【发布时间】:2009-08-09 20:00:49
【问题描述】:

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

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-statement 似乎都不是一个足够强的线索,可以让编译器永远不会生成 0x0 矩阵。 (具有讽刺意味的是,它确实抱怨 if-statement 具有恒定的编译时条件。)

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

【问题讨论】:

    标签: c++ templates recursion matrix


    【解决方案1】:

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

    例如

    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 矩阵归约的结果),您可以使用完全空的类来停止递归。

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

    【讨论】:

    • 他说 2x2 是最小的,但我仍然认为这是最好的。可以添加const int MinimumRowsconst int MinimumColumns,这样就可以调整了。
    • 谢谢,成功了。我更进一步,将Reduced 设为非成员函数,这样更容易隔离特化。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

        您需要明确指定您希望递归结束的情况的行为。有关详细信息,请参阅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 };
        };
        

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            猜你喜欢
            • 2020-11-10
            • 2014-09-10
            • 2012-10-26
            • 1970-01-01
            • 2013-06-05
            • 1970-01-01
            • 2021-07-31
            • 1970-01-01
            相关资源
            最近更新 更多