0

我正在尝试使用模板元编程、constexpr 和 if constexpr,并提出了 3 种不同的方法来进行 N 递归/N 阶乘运算。

所有三个示例都是我在 SO 上或通过在网上搜索找到的 - 然后对其进行了修改,所以它们也是一样的

第一个示例是使用模板元编程:示例 1

template<int N>
struct NGenerator
{
    static const int result = N + NGenerator<N-1>::result;
};

template<>
struct NGenerator<0>
{
    static const int result = 1; 
};

static int example1 = NGenerator<5>::result;

第二个仍在使用模板,但我在示例 2中抛出了一个 constexpr

template<int N>
constexpr int example2() 
{
return N + example2<N - 1>();
}


template<>
constexpr int example2<0>() 
{ 
    return 1;
}

static int ex2 = example2<5>();

第三个,是我删除模板并“仅”使用 constexpr 的地方:示例 3

constexpr int generator(int n)
{
    return (n <= 1) ? 1 : n + generator(n - 1);
}

static int ex3 = generator(5);

在我看来,所有三个都做同样的事情 - 当输入数字是编译时间常数时。这三个都是递归的,所有三个都在编译时工作。

我的问题是——这三者有什么区别?哪一个最可取?

最后——我想实现“if constexpr”但没能实现,所以我的解决方法是在示例 3 中执行“if 语句”——这并不是真正的 if 语句,但是如果 - 如果它以任何方式相同,我可以得到最接近编译时间,我不确定。

4

2 回答 2

2

像这样的玩具例子不会有太大的不同。对于更复杂的示例,递归模板实例化实际上具有与所有模板实例化名称(包括参数)的长度之和成正比的内存成本。这真的很容易炸。

根据我的经验constexpr,函数往往编译得更快。

编译器可能会记住所有 3 个;但是 constexpr 非模板函数会喷出更少的符号(噪音)。

比所有这些更好的是带有循环的 constexpr 函数。

编译器可以在运行时自由地执行大多数选项,因为您不坚持编译时评估。替换static intconstexpr int

于 2019-12-29T19:17:47.970 回答
1

示例 1 保证在编译时完成。

constexpr函数(example2 和 example3)可以在非 constexpr 上下文中调用,因此可以在运行时计算(编译器可能仍会优化它以在编译时使用 as-if 规则计算它)。

我会做类似的事情:

constexpr std::size_t factorial(std::size_t n)
{
    std::size_t res = 1;
    for (std::size_t i = 1; i < n; ++i) {
        res *= i;
    }
    return res;
}

template <std::size_t N>
static constexpr std::size_t Factorial = factorial(5);

static std::size_t ex4 = Factorial<5>;

C++20 增加consteval了只允许 constexpr 评估。

于 2019-12-29T19:19:45.993 回答