C++ 为什么我必须专门化递归模板变量?

C++ 为什么我必须专门化递归模板变量?,c++,recursion,metaprogramming,template-specialization,template-variables,C++,Recursion,Metaprogramming,Template Specialization,Template Variables,所以我在这里写了一个答案:它努力在编译时计算log2,如下所示: template <unsigned int x> constexpr enable_if_t<x != 0U, int> log2 = 1 + log2<x / 2U>; template <> constexpr int log2<1U> = 0; 模板 如果log2=1+log2,则constexpr启用; 模板 constexpr int log2=0; 这

所以我在这里写了一个答案:它努力在编译时计算
log2
,如下所示:

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

template <>
constexpr int log2<1U> = 0;
模板
如果log2=1+log2,则constexpr启用;
模板
constexpr int log2=0;
这很好,但我不觉得我应该专攻:

template <unsigned int x>
constexpr enable_if_t<x != 0U, int> log2 = x < 4U ? 1 : 1 + log2<x / 2U>;
模板
如果log2=x<4U,constexpr是否启用?1:1+log2;
但这是:

使用enable_if_t=typename std::enable_if::type[带bool_Cond=(0u!=0u);_Tp=int]替换
模板。

程序cpp:7:61:从
constepr std::enable_if_t log2

程序cpp:7:61:需要从
constexpr std::enable_if_t log2

进度cpp:10:11:从这里开始需要 /usr/include/c++/6/type_traits:2523:61:错误:在
struct std::enable_if


有什么方法可以防止编译器将递归展开得太远吗?

您可以使用递归来计算log2。我们生活中的每一个递归操作都需要leaf case

在递归叶函数的情况下,叶案例可以提供非递归返回。但是,对于模板变量,提供叶案例的唯一方法是专门化,没有其他方法

我相信,使用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
#包括
constexpr int log2(int arg){
如果(arg==0)返回0;
如果(arg==1)返回0;
返回1+log2(arg/2u);
}
constexpr std::积分常数z;//z、 值==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>{});
#包括
模板
constexpr int log2(标准::积分常数){
静态断言(arg>0,“错误arg到log2!”);
如果constexpr(arg==1){
返回0;
}否则{
返回1+log2(std::integral_常量{});
}
}
int k=log2(std::积分常数{});

有没有办法防止编译器将递归展开得太远?是的,通过为不进行递归实例化的基本情况添加专门化。@MilesBudnek我不想为
log2
定义它,你是说有更好的方法来实现这一点吗?没关系,我错误地将基本情况理解为0。如果有意义,
启用\u。您的模板是递归的。对于我们生活中的任何递归,我们都需要一个leaf case。在TMP中,提供叶案例的唯一方法是专门化。如果用constexpr函数和constexpr If替换TMP,就可以避免专门化。@JonathanMee用一些代码给出了相同效果的答案。所以我非常喜欢那里的
log2
函数,但是如果我想防止在运行时调用它呢?@JonathanMee是第一个?函数本身没有。第二个是自然编译时。