为什么我必须专门处理递归模板变量?

时间:2019-06-12 20:09:27

标签: c++ recursion metaprogramming template-specialization template-variables

所以我在这里写了一个答案:https://stackoverflow.com/a/56569397/2642059,它努力在编译时计算log2,如下所示:

template <unsigned int x>
constexpr enable_if_t<x != 0U, int> log2 = 1 + log2<x / 2U>;

template <>
constexpr int log2<1U> = 0;

这很好,但是我觉得我不应该专攻:

template <unsigned int x>
constexpr enable_if_t<x != 0U, int> log2 = x < 4U ? 1 : 1 + log2<x / 2U>;

但这gives me the error

  

代替template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = (0u != 0u); _Tp = int]
  prog.cpp:7:61:从constexpr std::enable_if_t<true, int> log2<4u>递归地要求
  prog.cpp:7:61:必须来自constexpr std::enable_if_t<true, int> log2<8u>
  prog.cpp:10:11:从这里开始   / usr / include / c ++ / 6 / type_traits:2523:61:错误:type中没有名为struct std::enable_if<false, int>的类型

有没有办法防止编译器将递归展开得太远?

1 个答案:

答案 0 :(得分:2)

您使用递归来计算log2。我们生活中的每一个递归操作都需要叶子实例。

在使用递归叶函数的情况下,可以为叶例提供非递归返回。但是,使用模板变量,提供叶子情况的唯一方法是进行专业化处理,根本没有其他方法。

我相信,使用constexpr函数而不使用TMP可以实现非常相同的目标:

#include <type_traits>

constexpr int log2(int arg) {
    if (arg == 0) return 0;
    if (arg == 1) return 0;
    return 1 + log2(arg / 2u);
}

constexpr std::integral_constant<int, log2(16)> z; // z.value == 4

这既适用于运行时参数,也适用于编译时参数,除教育目的外,通常应优于纯TMP解决方案。

出于教育或其他未公开的目的,您可以使用排他的编译时,例如:

#include <type_traits>

template<int arg>
constexpr int log2(std::integral_constant<int, arg> ) {
    static_assert(arg > 0, "Bad arg to log2!");
    if constexpr (arg == 1) {
        return 0;
    } else {
        return 1 + log2(std::integral_constant<int, arg / 2> {});
    }
}

int k = log2(std::integral_constant<int, 16>{});