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

发布于 2024-07-30 03:00:10 字数 995 浏览 4 评论 0原文

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

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 语句具有恒定的编译时条件。)

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

I have a matrix class with the size determined by template parameters.

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

My program uses matrices of a few sizes, typically 2x2, 3x3, and 4x4. By setting the matrix size with template parameters rather than run-time parameters allows the compiler to do a lot of inlining and optimization.

But now I need a member function that returns a new matrix that has one fewer row and one fewer column.

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

The idea is that that it will return a matrix with the specified row and column deleted. In practice, this will only ever be called with a matrix that has at least three rows and three columns, returning a 2x2 at the smallest.

The compiler doesn't see the lower bound, so it gets stuck in an infinite recursion trying to instantiate the templates with ever decreasing sizes. I tried putting two clues in the function itself that these smaller sizes cannot occur:

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;
}

Neither the static_assert nor the if-statement seems to be a strong enough clue to the compiler that a 0x0 matrix will never be generated. (Ironically, it does complain about the if-statement having a constant compile-time condition.)

Does anyone have any suggestions on how to avoid this compile-time infinite recursion?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

甜宝宝 2024-08-06 03:00:10

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

例如,

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>(); }
};

您遇到的问题是,尝试使用一组特定的模板参数实例化矩阵缩减函数始终需要为另一组参数实例化矩阵模板(cRows - 1,cCols -1)。 这个递归必须在某个地方停止。 如果您只处理方阵,那么您可以采用较少的专业化。

另外,如果您永远不会使用 1x1 矩阵(例如 2x2 矩阵上的 reduce 的结果),则可以使用完全空的类来停止递归。

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

You need to provide a specialization for a Matrix that has no rows or no columns.

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>(); }
};

The issue you have is that attempting to instantiate the Matrix Reduced function with a particular set of template parameters always required instantiating the Matrix template for a different set of parameters (cRows - 1, cCols -1). This recursion has to be stopped somewhere. If you are only ever dealing with square matrices, then you can get away with fewer specializations.

Also, you can could stop the recursion with a completely empty class if you are never going to use, say, a 1x1 matrix, the result of reduce on a 2x2 matrix.

template<>
class Matrix< 1, 1 > {};
玉环 2024-08-06 03:00:10

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

You can specify a template specializations for small values of cRows or cCols which does not include that method.

你在我安 2024-08-06 03:00:10

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

如果您还没有读过,我建议您阅读
C++ 模板:完整指南 作者:Vandervoorde & Josuttis,其中详细介绍了此类事情。

You seem a bit confused about compile time and run time behaviour, and I'm a bit confused by your code, but I think what you want is a specialisation of the template for the values, 0, 0, which terminates the recursion.

If you haven't already got it, I suggest reading
C++ Templates: The Complete Guide by Vandervoorde & Josuttis, which covers this sort of thing in detail.

请止步禁区 2024-08-06 03:00:10

您需要显式指定希望递归结束的情况的行为。 请参阅这篇 DDJ 文章了解更多详细信息。 这是文章中的一个简单示例:

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

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

You need to explicitly specify the behaviour for the case where you want the recursion to end. See this DDJ article for more details. Here is a simple example from the 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 };
};
挽你眉间 2024-08-06 03:00:10

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

Rather than specializing the entire class to terminate the recursion, another option might be to use boost::enable_if on the function to make it available only when the matrix size is above 2x2.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文