Ara*_*raK 6 c++ recursion templates metaprogramming square-root
美好的一天,
我的一个朋友正在询问将整数平方根函数转换为元函数.这是原始功能:
unsigned isqrt(unsigned value)
{
unsigned sq = 1, dlt = 3;
while(sq<=value)
{
sq += dlt;
dlt += 2;
}
return (dlt>>1) - 1;
}
Run Code Online (Sandbox Code Playgroud)
我使用了一个元版本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);
}
Run Code Online (Sandbox Code Playgroud)
所以我认为将其转换为以递归方式调用它的模板结构应该不难:
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;
};
Run Code Online (Sandbox Code Playgroud)
不幸的是,这导致无限递归(在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
Run Code Online (Sandbox Code Playgroud)
谢谢大家,
不幸的是,这导致无限递归(在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;
};
Run Code Online (Sandbox Code Playgroud)
默认情况下,模板评估不是惰性的。
static const std::size_t square_root =
sq <= value ?
isqrt_impl<value, sq+dlt, dlt+2>::square_root :
(dlt >> 1) - 1;
Run Code Online (Sandbox Code Playgroud)
无论条件如何,都将始终实例化模板。您需要boost::mpl::eval_if或等效的东西才能使该解决方案发挥作用。
或者,您可以使用基本情况部分模板专业化,如果满足条件,则停止递归,就像 K-ballos 答案中一样。
实际上,与部分专业化相比,我更喜欢使用某种形式的惰性求值的代码,因为我觉得它更容易理解,并且可以降低模板带来的噪音。