C++ VS2015和GCC 5.4.0中的Constexpr阶乘编译结果

C++ VS2015和GCC 5.4.0中的Constexpr阶乘编译结果,c++,c++11,gcc,visual-studio-2015,constexpr,C++,C++11,Gcc,Visual Studio 2015,Constexpr,想知道下面的事情是否会让任何人感到惊讶,就像我一样?Alex Allain关于使用constexpr的文章展示了以下阶乘示例: constexpr factorial (int n) { return n > 0 ? n * factorial( n - 1 ) : 1; } 并指出: 现在您可以使用阶乘(2),当编译器看到它时,它可以 优化调用,并在编译时完全进行计算 时间 我在VS2015的发布模式下尝试了这一点,在(/Ox)上进行了全面优化,并在查看程序集的调试器中逐步查看

想知道下面的事情是否会让任何人感到惊讶,就像我一样?Alex Allain关于使用constexpr的文章展示了以下阶乘示例:

constexpr factorial (int n)
{
    return n > 0 ? n * factorial( n - 1 ) : 1;
}
并指出:

现在您可以使用阶乘(2),当编译器看到它时,它可以 优化调用,并在编译时完全进行计算 时间

我在VS2015的发布模式下尝试了这一点,在(/Ox)上进行了全面优化,并在查看程序集的调试器中逐步查看了代码,发现阶乘计算在编译时没有完成

使用gccv5.4.0和--std=C++14,我必须在编译时执行计算之前使用/O2或/O3。我感到惊讶的是,使用just/O计算并没有在编译时进行


主要问题是:为什么VS2015在编译时不执行此计算

您必须在常量表达式中使用它,如下所示:

 constexpr auto res = factorial(2);

否则计算可以在运行时完成。

这取决于函数调用的上下文

例如,在编译时显然无法计算以下内容:

int x;
std::cin >> x;
std::cout << factorial(x);
class Foo {
    int x[factorial(4)];
};
constexpr
函数只有在从
constexpr
上下文调用时才能保证在编译时进行计算;否则,由编译器选择是否在编译时求值(假设这种优化也是可能的,取决于上下文)。

constexpr
对于函数的编译时求值既不必要也不充分

这是不够的,即使撇开参数显然也必须是常量表达式这一事实不谈。即使这是真的,一致性编译器也不必在编译时对其求值。如果它位于constexpr上下文中,则只需在编译时对其求值。例如,将计算结果指定给
constepr
变量,或将该值用作数组大小或非类型模板参数

另一点是,编译器完全能够在编译时计算内容,即使没有constexpr。关于这一点有很多困惑,但不清楚原因。编译时对
constexpr
函数的评估基本上可以归结为不断的传播,编译器从永远以来都在进行这种优化:


本质上,除了通过将
constepr
函数分配给另一个constepr变量来强制编译时计算的特定情况之外,编译时评估与优化器的质量有关,而与标准无关。

您的资格有点强:只有在constexpr上下文中调用constexpr函数时,才能保证在编译时对其进行评估。编译器可以自由地在任何其他地方进行优化,因为它可以或认为合适;只要编译器有能力并决定执行,就可以进行计算。早在constexpr存在之前,编译器就已经开始进行const传播了。@NirFriedman:我并没有说编译器必须在运行时进行计算,否则的话,只是编译器必须在编译时在比预期更少的地方进行计算
constexpr
不是唯一的地方,还有数组大小、模板参数…@Jarod42-是的,似乎就是这样。在VS2015(/Ox)中重新编译时,在构造返回值时使用constexpr会导致在编译时进行计算。谢谢你指出这一点。
int factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n-1);
}

int foo() { return factorial(5); }
foo():
        mov     eax, 120
        ret