【问题标题】:Constexpr variable evaluationConsexpr 变量评估
【发布时间】:2015-10-01 03:30:22
【问题描述】:

这是我的代码,我需要澄清发生了什么:

constexpr int funct(int x){
    return x + 1;
}


int main(){
    int x = funct(10);
    return 0;
}

constexpr's 允许编译时间计算,根据我上面的代码,由于funct 被声明为constexpr,如果参数是常量或 constexpr 本身,则允许进行编译时间计算。

我感到困惑的部分在于这部分,int x。由于它没有被声明为constexpr,这是否意味着int x 将在运行时获得该值?这是否意味着将其声明为 constexpr int x 将意味着 int x 将在编译时获得与 int x 不同的值?

【问题讨论】:

  • 会有什么不同?或者,你怎么能分辨出来? x 是一个局部变量,它必须在运行时初始化。
  • 假设没有优化,是的,声明是在运行时评估的。但是,允许编译器将这个程序优化到什么都不做,因为它什么都不做。
  • 啊,我明白了。所以如果它是int x,那么它肯定会在运行时,constexpr 会在编译时?
  • @CarloBrew 否。constexpr 函数仍可用作具有运行时相关参数的普通函数。参见例如。 ideone.com/3fldYyconstexpr 表示对于某个输入,输出每次都是相同的(并且对函数内容有一些限制),并且在编译时已知输入,这可以启用一些优化等。但是,如前所述,没有什么能阻止你传递编译时未知的东西。

标签: c++ constexpr compile-time compile-time-constant


【解决方案1】:

这在很多方面取决于所讨论的编译器。可能会发生什么样的优化等。但是,constexpr 本身并不支持编译时计算。

获取此代码:

#include <cstdio>

constexpr int test(int in)
{
   return in + 25;
}

int main(int argc, char* argv[])
{
   printf("Test: %u\n", test(5));
   printf("Two: %u\n", test(10));
}

在我的 x86_64 Gentoo 机器上的 GCC 4.8.4 下,实际上仍然会调用所谓的编译时“测试”。我使用的线条是

g++ -std=c++11 -Wall -g  -c main.cpp -o obj/Debug/main.o
g++  -o bin/Debug/TestProject obj/Debug/main.o   

所以在上面的代码中,会产生以下机器代码:

0x40061c    mov    edi,0x5
0x400621    call   0x400659 <test(int)>
0x400626    mov    esi,eax
0x400628    mov    edi,0x4006f4
0x40062d    mov    eax,0x0
0x400632    call   0x4004f0 <printf@plt>
0x400637    mov    edi,0xa
0x40063c    call   0x400659 <test(int)>
0x400641    mov    esi,eax
0x400643    mov    edi,0x4006fd
0x400648    mov    eax,0x0
0x40064d    call   0x4004f0 <printf@plt>

“test”的 asm 块在哪里:

0x400659    push   rbp 
0x40065a    mov    rbp,rsp
0x40065d    mov    DWORD PTR [rbp-0x4],edi
0x400660    mov    eax,DWORD PTR [rbp-0x4]
0x400663    add    eax,0x19
0x400666    pop    rbp
0x400667    ret

因此,正如您在那种情况下所看到的,它似乎对 GCC 生成该代码的方式几乎没有影响。即使你这样做了,你仍然会得到运行时计算:

int value = test(30);
printf("Value: %u\n", value);

产生这种情况的地方(由于添加了更多代码,测试地址略有变化):

0x40061c    mov    edi,0x1e
0x400621    call   0x40067a <test(int)>
0x400626    mov    DWORD PTR [rbp-0x4],eax
0x400629    mov    eax,DWORD PTR [rbp-0x4]
0x40062c    mov    esi,eax
0x40062e    mov    edi,0x400714
0x400633    mov    eax,0x0
0x400638    call   0x4004f0 <printf@plt>

但是,如果您将值 itself 声明为 constexpr,它确实会产生预期的结果:

constexpr int value = test(30);
printf("Value: %u\n", value);

而相关代码为:

0x400623    mov    esi,0x37
0x400628    mov    edi,0x400714
0x40062d    mov    eax,0x0
0x400632    call   0x4004f0 <printf@plt>

因此,基本上,如果您只是在方法声明前加上 constexpr,则无法保证编译时计算。您还需要将您的变量声明为 constexpr 并分配给它。执行这样的声明实际上将要求编译器静态评估结果。如果你尝试这样做并且“test”没有声明为 constexpr,GCC 实际上会对你大喊大叫:

main.cpp|10|错误:调用非 constexpr 函数‘int test(int)’|

【讨论】:

  • 我明白了,谢谢你的详细解释。我会研究一下。
  • 只是一个后续问题,如果我构造一个 constexpr 构造函数,它们会自动 constexpr 吗?
  • 在同一个编译器下,如果构造函数被声明为 constexpr,它似乎什么都不做。如下所示的构造函数: constexpr Test(int a, int b) : mA(a + 10), mB(b + 20) { } 仍将根据 GCC 为我生成的程序集在运行时计算 mA 和 mB .简单地将 new 本身声明为 constexpr 本身就会失败,因为它是动态内存分配,因此编译器无法知道在这些情况下将返回什么内存地址。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-23
  • 2010-10-07
  • 1970-01-01
  • 2016-07-05
  • 1970-01-01
相关资源
最近更新 更多