C++ constexpr深度限制与叮当声(fconstexpr深度似乎不起作用)

C++ constexpr深度限制与叮当声(fconstexpr深度似乎不起作用),c++,c++11,clang,constexpr,c++14,C++,C++11,Clang,Constexpr,C++14,是否仍然需要配置constexpr实例化深度? 我使用-fconstexpr depth=4096(使用clang/XCode)运行 但仍然无法编译此代码,出现错误: Constexpr变量fib_1必须由常量表达式初始化。 无论是否设置了-fconstexpr depth=4096选项,代码都会失败 这是一个带有叮当声的bug,还是预期会有这样的行为。 注意:在fib_cxpr(26)和27开始失效之前,这都是有效的 代码: TL;博士: 对于clang,您需要命令行参数-fconstexpr

是否仍然需要配置constexpr实例化深度? 我使用-fconstexpr depth=4096(使用clang/XCode)运行

但仍然无法编译此代码,出现错误: Constexpr变量fib_1必须由常量表达式初始化。 无论是否设置了-fconstexpr depth=4096选项,代码都会失败

这是一个带有叮当声的bug,还是预期会有这样的行为。 注意:在fib_cxpr(26)和27开始失效之前,这都是有效的

代码:

TL;博士:

对于clang,您需要命令行参数
-fconstexpr steps=1271242
,并且只需要
-fconstexpr depth=27


计算斐波那契数的递归方法不需要太多的递归深度。
fib(n)
所需的深度实际上不超过
n
。这是因为最长的调用链是通过
fib(i-1)
递归调用的

constexpr auto fib_1 = fib_cxpr(3); // fails with -fconstexpr-depth=2, works with -fconstexpr-depth=3
constexpr auto fib_1 = fib_cxpr(4); // fails with -fconstexpr-depth=3, works with -fconstexpr-depth=4
因此,我们可以得出结论,
-fconstexpr depth
不是重要的设置

此外,错误消息还指示一个差异:

constexpr auto fib_1 = fib_cxpr(27);
编译时使用
-fconstexpr depth=26
,为了确保达到该限制,clang生成以下消息:

note: constexpr evaluation exceeded maximum depth of 26 calls
note: constexpr evaluation hit maximum step limit; possible infinite loop?
但使用足够深度的
-fconstexpr depth=27进行编译时,会生成以下消息:

note: constexpr evaluation exceeded maximum depth of 26 calls
note: constexpr evaluation hit maximum step limit; possible infinite loop?
所以我们知道,clang区分了两种失败:递归深度和“步长限制”


谷歌“clang maximum step limit”的最热门搜索结果显示了关于实现此功能的clang补丁的页面,包括命令行选项的实现:
-fconstexpr steps
。进一步搜索此选项表明没有用户级文档

因此,关于什么样的叮当算是一个“步骤”,或者对于
fib(27)
,叮当需要多少个“步骤”,没有任何文档。我们可以把这个设定得很高,但我认为这是个坏主意。相反,一些实验表明:

n : steps
0 : 2
1 : 2
2 : 6
3 : 10
4 : 18
这表示步骤(
fib(n)
)==步骤(
fib(n-1)
)+步骤(
fib(n-2)
)+2。一点计算表明,根据这一点,
fib(27)
应该需要1271242个clang步骤。因此,使用
-fconstexpr steps=1271242
编译应该允许程序进行编译,这确实是可行的。使用
-fconstexpr steps=1271241
编译会产生与以前相同的错误,因此我们知道我们有一个确切的限制

另一种不太精确的方法是从补片上观察默认步长限制为1048576(220),这显然足以满足
fib(26)
。直觉上,将这一数字翻一番就足够了,从早期的分析中我们知道200万就足够了。严格的限制是必要的⌈φ·步(
fib(26)
)⌉ (正好是1271242)



另一件需要注意的事情是,这些结果清楚地表明,clang并没有对constexpr评估进行任何记忆。GCC,但它似乎根本没有在clang中实现。尽管记忆增加了内存需求,但有时,如本例所示,它可以大大减少评估所需的时间。我从中得出的两个结论是,编写需要记忆以获得良好编译时间的constexpr代码对于可移植代码不是一个好主意,通过支持constexpr记忆和一个命令行选项来启用/禁用它,这种叮当声可以得到改进。

您还可以重构斐波那契算法,以包括在叮当声中工作的显式记忆

// Copyright 2021 Google LLC.
// SPDX-License-Identifier: Apache-2.0

#include <iostream>

template <int idx>
constexpr int fib_cxpr();

// This constexpr template value acts as the explicit memoization for the fib_cxpr function.
template <int i>
constexpr int kFib = fib_cxpr<i>();

// Arguments cannot be used in constexpr contexts (like the if constexpr),
// so idx is refactored as a template value argument instead.
template <int idx>
constexpr int fib_cxpr() {
    if constexpr (idx == 0 || idx == 1) {
        return idx;
    } else {
        return kFib<idx-1> + kFib<idx-2>;
    }      
}

int main() {
    constexpr auto fib_1 = fib_cxpr<27>();
    std::cout << fib_1 << "\n";
    return 0; 
}
//版权所有2021谷歌有限责任公司。
//SPDX许可证标识符:Apache-2.0
#包括
模板
constexpr int fib_cxpr();
//此constexpr模板值用作fib_cxpr函数的显式记忆。
模板
constexpr int kFib=fib_cxpr();
//参数不能在constexpr上下文中使用(如if constexpr),
//因此idx被重构为一个模板值参数。
模板
constexpr int fib_cxpr(){
如果constexpr(idx==0 | | idx==1){
返回idx;
}否则{
返回kFib+kFib;
}      
}
int main(){
constexpr auto fib_1=fib_cxpr();
std::cout与“深度限制”无关,但与斐波那契数计算密切相关

递归可能是错误的方法,不需要

有一种超快的解决方案,可以降低内存占用

因此,我们可以使用一个64位值的所有斐波那契数的编译时预计算

Fibonacci数列的一个重要特性是值呈指数增长,因此,所有现有内置整数数据类型都会很快溢出

使用,您可以计算第93个斐波那契数是最后一个适合64位无符号值的数

在编译过程中计算93个值是一项非常简单的任务

我们首先将计算斐波那契数的默认方法定义为
constepr
函数:

// Constexpr function to calculate the nth Fibonacci number
constexpr unsigned long long getFibonacciNumber(size_t index) noexcept {
    // Initialize first two even numbers 
    unsigned long long f1{ 0 }, f2{ 1 };

    // calculating Fibonacci value 
    while (index--) {
        // get next value of Fibonacci sequence 
        unsigned long long f3 = f2 + f1;
        // Move to next number
        f1 = f2;
        f2 = f3;
    }
    return f2;
}
这样,斐波那契数就可以在编译时轻松计算出来。然后,我们用所有斐波那契数填充
std::array
。我们还使用
constepr
,并将其作为带有可变参数包的模板

我们使用
std::integer_序列
为索引0,1,2,3,4,5等创建斐波那契数

这是严格的,并不复杂:

template <size_t... ManyIndices>
constexpr auto generateArrayHelper(std::integer_sequence<size_t, ManyIndices...>) noexcept {
    return std::array<unsigned long long, sizeof...(ManyIndices)>{ { getFibonacciNumber(ManyIndices)... } };
};
将为我们提供一个编译时
std::array
,名称FIB包含所有斐波那契数。如果我们需要第i个斐波那契数,那么我们只需编写
FIB[i]
。在运行时将不会进行计算

我不认为有更快的方法来计算第n个斐波那契数

请参阅下面的完整程序:

#include <iostream>
#include <array>
#include <utility>
// ----------------------------------------------------------------------
// All the following will be done during compile time

// Constexpr function to calculate the nth Fibonacci number
constexpr unsigned long long getFibonacciNumber(size_t index) {
    // Initialize first two even numbers 
    unsigned long long f1{ 0 }, f2{ 1 };

    // calculating Fibonacci value 
    while (index--) {
        // get next value of Fibonacci sequence 
        unsigned long long f3 = f2 + f1;
        // Move to next number
        f1 = f2;
        f2 = f3;
    }
    return f2;
}
// We will automatically build an array of Fibonacci numberscompile time
// Generate a std::array with n elements 
template <size_t... ManyIndices>
constexpr auto generateArrayHelper(std::integer_sequence<size_t, ManyIndices...>) noexcept {
    return std::array<unsigned long long, sizeof...(ManyIndices)>{ { getFibonacciNumber(ManyIndices)... } };
};

// Max index for Fibonaccis that for in an 64bit unsigned value (Binets formula)
constexpr size_t MaxIndexFor64BitValue = 93;

// Generate the required number of elements
constexpr auto generateArray()noexcept {
    return generateArrayHelper(std::make_integer_sequence<size_t, MaxIndexFor64BitValue>());
}

// This is an constexpr array of all Fibonacci numbers
constexpr auto FIB = generateArray();
// ----------------------------------------------------------------------

// Test
int main() {

    // Print all possible Fibonacci numbers
    for (size_t i{}; i < MaxIndexFor64BitValue; ++i)

        std::cout << i << "\t--> " << FIB[i] << '\n';

    return 0;
}
#包括
#包括
#包括
// ----------------------------------------------------------------------
//以下所有操作将在
#include <iostream>
#include <array>
#include <utility>
// ----------------------------------------------------------------------
// All the following will be done during compile time

// Constexpr function to calculate the nth Fibonacci number
constexpr unsigned long long getFibonacciNumber(size_t index) {
    // Initialize first two even numbers 
    unsigned long long f1{ 0 }, f2{ 1 };

    // calculating Fibonacci value 
    while (index--) {
        // get next value of Fibonacci sequence 
        unsigned long long f3 = f2 + f1;
        // Move to next number
        f1 = f2;
        f2 = f3;
    }
    return f2;
}
// We will automatically build an array of Fibonacci numberscompile time
// Generate a std::array with n elements 
template <size_t... ManyIndices>
constexpr auto generateArrayHelper(std::integer_sequence<size_t, ManyIndices...>) noexcept {
    return std::array<unsigned long long, sizeof...(ManyIndices)>{ { getFibonacciNumber(ManyIndices)... } };
};

// Max index for Fibonaccis that for in an 64bit unsigned value (Binets formula)
constexpr size_t MaxIndexFor64BitValue = 93;

// Generate the required number of elements
constexpr auto generateArray()noexcept {
    return generateArrayHelper(std::make_integer_sequence<size_t, MaxIndexFor64BitValue>());
}

// This is an constexpr array of all Fibonacci numbers
constexpr auto FIB = generateArray();
// ----------------------------------------------------------------------

// Test
int main() {

    // Print all possible Fibonacci numbers
    for (size_t i{}; i < MaxIndexFor64BitValue; ++i)

        std::cout << i << "\t--> " << FIB[i] << '\n';

    return 0;
}