【发布时间】:2013-06-03 03:11:36
【问题描述】:
在 clang 下,以下函数为尾递归函数生成目标代码:
template<typename T>
constexpr bool is_prime(T number, T limit, T counter)
{
return counter >= limit
? number % limit != 0
: number % counter
? is_prime(number, number / counter, counter + 2)
: false;
}
template<typename T>
constexpr bool is_prime(T n)
{
return n == 2 || n == 3 || n == 5
? true
: n <= 1 || n % 2 == 0
? false
: is_prime(n, n / 3, T{3});
}
但改变一行(让布尔结果“非规范化”),它停止生成尾递归目标代码:
template<typename T>
constexpr bool is_prime(T number, T limit, T counter)
{
return counter >= limit
? number % limit // changed here
: number % counter
? is_prime(number, number / counter, counter + 2)
: false;
}
template<typename T>
constexpr bool is_prime(T n)
{
return n == 2 || n == 3 || n == 5
? true
: n <= 1 || n % 2 == 0
? false
: is_prime(n, n / 3, T{3});
}
是 clang 未能正确优化它还是有合理的原因?
为了强制运行时评估,x 是运行时整数素数 ≥ 13(一次递归)或足够大的 constexpr 素数,由于递归深度大,会阻止编译时评估:
is_prime(x);
【问题讨论】:
-
删除 != 0 意味着三元语句的结果可能不是布尔值。结果必须转换为正确的类型,因此递归调用不是最后一个语句。
-
@krsteeve 我以前想过,但这对我来说没有意义。这是一个简单的分支,如果执行不到
number % limit,它将被丢弃,因此,不需要为下一次调用保存状态,它可以被丢弃,不管它是否与!= 0,在我看来。 -
经过一番挖掘 - 如果你有 exp1 ? exp2 : exp3, 判断 ?: 语句的类型,将 exp3 的类型转换为 exp2 的类型(如果可能)。这意味着您的漂亮 bool 结果被转换为类型 T 并且必须转换回 bool 才能返回。我很好奇如果你反转你的 ?: 声明会发生什么
标签: c++ clang tail-recursion