返回语句中的C ++ constexpr函数

时间:2019-01-14 12:43:34

标签: c++ gcc clang constexpr

为什么在编译时不对constexpr函数求值,而在运行时在主函数的return语句中求值?

它尝试了

ItemsSource

结果是

<toolkit:AutoCompleteBox x:Name="boxbox" Height="23"
  ItemsSource="{Binding surname}"
SelectedItem="{Binding surname, Mode=TwoWay}" Margin="93,38,119,95" />

使用gcc 8.2。但是当我在return语句

中调用该函数时
template<int x>
constexpr int fac() {
    return fac<x - 1>() * x; 
} 

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

int main() {
    const int x = fac<3>();
    return x;
} 

我知道

main:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 6
        mov     eax, 6
        pop     rbp
        ret

为什么在编译时评估第一个代码,而在运行时评估第二个代码?

我还尝试了clang 7.0.0的两个代码片段,并在运行时对其进行了评估。为什么这对于clang无效的constexpr?

所有评估均在Godbolt编译器资源管理器中完成。

2 个答案:

答案 0 :(得分:26)

关于constexpr的一个常见误解是“这将在编译时进行评估” 1

不是。引入constexpr是为了让我们编写自然的代码,这些代码可以在需要它们的上下文中生成常量表达式。这表示“这必须在编译时才能评估” ,这是编译器将检查的内容。

因此,如果您编写了一个返回整数的constexpr函数,则可以使用它来计算模板参数,constexpr变量的初始化程序(如果是整数类型,则为const )或数组大小。您可以使用该函数获取自然的,说明性的,可读的代码,而不用使用过去需要使用的旧元编程技巧。

但是constexpr函数仍然是常规函数。 constexpr说明符并不意味着编译器拥有 2 可以对其进行优化以使其在编译时进行折叠并进行恒定折叠。最好不要将其混淆为这样的提示。


1 -感谢user463035818的措词。
2 -consteval是另外一个故事:)

答案 1 :(得分:12)

StoryTeller的回答很好,但是我认为可能会有一点不同。

使用constexpr,可以区分三种情况:

  1. 在编译时上下文中需要结果,例如数组大小。在这种情况下,也必须在编译时知道参数。评估是在编译时可能,并且至少所有可诊断的错误都将在编译时发现。

  2. 仅在运行时知道参数,而在编译时不需要结果。在这种情况下,评估必须在运行时进行。

  3. 这些参数可能在编译时可用,但仅在运行时需要结果。

第四个组合(参数仅在运行时可用,编译时需要结果)是错误;编译器将拒绝此类代码。

现在,在情况1和3中,由于所有输入均可用,因此计算可以在编译时进行。但是为了方便第2种情况,编译器必须能够创建运行时版本,并且在可能的情况下,它可能会决定在其他情况下也使用此变体。

例如一些编译器内部支持可变大小的数组,因此即使该语言需要编译时数组范围,实现也可能会决定不这样做。