【问题标题】:Infinite Recursion in Meta Integer Square Root元整数平方根中的无限递归
【发布时间】:2011-10-17 15:27:15
【问题描述】:

早安,

我的一个朋友正在询问如何将整数平方根函数转换为元函数。这是原始函数:

unsigned isqrt(unsigned value)
{
    unsigned sq = 1, dlt = 3;
    while(sq<=value)
    {
        sq  += dlt;
        dlt += 2;
    }
    return (dlt>>1) - 1;
}

我使用constexpr写了一个元版本,但他说他不能使用新功能,因为某种原因:

constexpr std::size_t isqrt_impl
    (std::size_t sq, std::size_t dlt, std::size_t value){
    return sq <= value ?
        isqrt_impl(sq+dlt, dlt+2, value) : (dlt >> 1) - 1;
}

constexpr std::size_t isqrt(std::size_t value){
    return isqrt_impl(1, 3, value);
}

所以我认为将其转换为以递归方式调用它的模板结构应该不难:

template <std::size_t value, std::size_t sq, std::size_t dlt>
struct isqrt_impl{
    static const std::size_t square_root = 
        sq <= value ?
        isqrt_impl<value, sq+dlt, dlt+2>::square_root :
        (dlt >> 1) - 1;
};

template <std::size_t value>
struct isqrt{
    static const std::size_t square_root = 
        isqrt_impl<value, 1, 3>::square_root;
};

不幸的是,这会导致无限递归(在 GCC 4.6.1 上),我无法弄清楚代码有什么问题。这是错误:

 C:\test>g++ -Wall test.cpp
test.cpp:6:119: error: template instantiation depth exceeds maximum of 1024 (use
 -ftemplate-depth= to increase the maximum) instantiating 'struct isqrt_impl<25u
, 1048576u, 2049u>'
test.cpp:6:119:   recursively instantiated from 'const size_t isqrt_impl<25u, 4u
, 5u>::square_root'
test.cpp:6:119:   instantiated from 'const size_t isqrt_impl<25u, 1u, 3u>::squar
e_root'
test.cpp:11:69:   instantiated from 'const size_t isqrt<25u>::square_root'
test.cpp:15:29:   instantiated from here

test.cpp:6:119: error: incomplete type 'isqrt_impl<25u, 1048576u, 2049u>' used i
n nested name specifier

谢谢大家,

【问题讨论】:

  • 如果使用递归函数,实际的递归深度是多少?
  • @sharptooth 任何值都会发生,并不是使用的值导致溢出。

标签: c++ templates recursion metaprogramming square-root


【解决方案1】:

不幸的是,这会导致无限递归(在 GCC 4.6.1 上),我无法弄清楚代码有什么问题。

我没有看到 isqrt_impl 的基本案例专业化。您需要对基本案例进行模板特化才能打破这种递归。这是一个简单的尝试:

template <std::size_t value, std::size_t sq, std::size_t dlt, bool less_or_equal = sq <= value >
struct isqrt_impl;

template <std::size_t value, std::size_t sq, std::size_t dlt>
struct isqrt_impl< value, sq, dlt, true >{
    static const std::size_t square_root = 
        isqrt_impl<value, sq+dlt, dlt+2>::square_root;
};

template <std::size_t value, std::size_t sq, std::size_t dlt>
struct isqrt_impl< value, sq, dlt, false >{
    static const std::size_t square_root = 
        (dlt >> 1) - 1;
};

【讨论】:

  • 非常感谢,感谢您的回答。我真的不明白为什么我首先需要专业化。这就是我选择@pmr 回答的原因,但您的回答非常好。我会让我的朋友看到​​你的回答作为他问题的解决方案。
  • @AraK :此站点允许您重新分配选定的答案。只需单击此答案旁边的空白“复选标记”即可。
【解决方案2】:

默认情况下模板评估不是惰性的。

static const std::size_t square_root = 
    sq <= value ?
    isqrt_impl<value, sq+dlt, dlt+2>::square_root :
    (dlt >> 1) - 1;

将始终实例化模板,无论条件如何。您需要 boost::mpl::eval_if 或类似的东西才能使该解决方案发挥作用。

或者,您可以有一个基本案例部分模板特化,如果满足条件,则停止递归,就像在 K-ballos 答案中一样。

我实际上更喜欢使用某种形式的惰性求值而不是部分专业化的代码,因为我觉得它更容易理解并且模板带来的噪音更低。

【讨论】:

  • 谢谢,我不知道无论三元运算符中的条件如何,模板都会被实例化。现在很清楚了。
  • @AraK 我将用 K-ballos 解决方案补充我的答案,以获得全面的公认答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-28
相关资源
最近更新 更多