C++:尝试通过组合和帕斯卡三角来理解 constexpr

C++: Trying to understand constexpr through combinations and Pascal's Triangle

我知道 constexpr 应该允许消除/简化过去使用的许多模板编程技巧,但我对 C++11 还是很陌生,我无法理解为什么,在计算组合函数时:

C(n,r) = n!/(r!(n-r)!) = C(n-1,r-1) + C(n-1,r)

递归方法,如预期的那样在运行时执行时速度慢得离谱,但允许计算 C(n,r) 更大的值,适用于模板,但我无法使用它constexpr。这是我正在使用的模板代码:

using factorial_t = unsigned long long;

template<size_t N, size_t R>
struct recursive_combinations {
    enum: factorial_t { value = recursive_combinations<N-1, R>::value +
                                recursive_combinations<N-1, R-1>::value};
};

template<size_t N>
struct recursive_combinations<N,0> {
    enum: factorial_t { value = 1 };
};

template<size_t N>
struct recursive_combinations<N,N> {
    enum: factorial_t { value = 1 };
};

这里是 constexpr 版本(也许我在这里做错了什么):

constexpr const factorial_t recursiveCombinations(const size_t N, const size_t R) {
    if (R == N || R == 0) return 1;
    return recursiveCombinations(N-1, R) + recursiveCombinations(N-1, R-1);
}

当我尝试这个时:

constexpr auto comb1_50_30 = recursive_combinations<50,30>::value;

一切正常,我得到了预期结果47,129,212,243,960,这是计算阶乘得不到的

但是,当我尝试这样做时:

constexpr auto comb2_50_30 = recursiveCombinations(50, 30);

编译器(clang v5.0.1,设置为C++17模式)抱怨comb2_50_30必须用常量表达式初始化。

谁能帮我弄清楚我做错了什么,或者是否有办法让它与 constexpr 一起工作(如果没有,为什么不呢)?

complains that comb2_50_30 must be initialized by a constant expression.

错误的下一行告诉你原因:

note: constexpr evaluation hit maximum step limit; possible infinite loop?

您的扩展对于 constexpr 评估来说计算量太大了。您可以使用 -fconstexpr-steps=X-fconstexpr-depth=X 来增加允许编译器计算 constexpr 函数的工作量,但我没能找到允许您的 recursiveCombinations(50, 30) 在 clang 上编译。

g++ 编译 constexpr 版本就好了。

以将结果的限制减少一个因子 min(r, n-r) 为代价,您可以使用以下更有效的实现:

constexpr uint64_t efficientCombinations(uint64_t n, uint64_t r)
{
    uint64_t accum = 1U;
    if (n - r > r) r = n - r;
    for( uint64_t x = 1; x <= n - r; ++x )
    {
        accum *= (r + x);
        accum /= x;
    }
    return accum;
}

为了与 C++11 兼容,将此迭代转换为递归很简单,即使有点混乱。

(注意:根据 "get this to work" 的问题的含义,像这样更改算法可能不是理想的方法...但它太大了,无法发表评论)