constexpr数组成员是否编译时间常量

Are constexpr array members compile time constants?

本文关键字:时间 常量 编译 是否 数组 组成员 constexpr      更新时间:2023-10-16

是代码片段

struct Parameters {
   static constexpr int n = 2;
   static constexpr double v[n] = {4.0, 5.0};
};

合法的C++11?如果是,Parameters::v[0]Parameters::v[1]是编译时常数,还是指针Parameters::v本身就是constexpr(无论在编译时意味着什么(?

正如您所看到的,我通常对constexpr数组及其在类/结构中的初始化有点困惑。请不仅回答我的具体问题,而且提及与此主题相关的常见陷阱等。

我认为构造没有问题。引用C++11,[dcl.constexpr]:

§1 constexpr说明符应仅适用于变量的定义、函数的声明或函数模板,或文字类型(3.9(的静态数据成员的声明…

§9对象声明中使用的constexpr说明符将对象声明为const。此类对象应具有文字类型,并且应进行初始化。如果它是由构造函数调用初始化的,则该调用应为常量表达式(5.19(。否则,或者如果在引用声明中使用了constexpr说明符,则每个完整表达式出现在其初始值设定项中的应该是一个常量表达式。中使用的每个隐式转换转换初始值设定项表达式,用于初始化的每个构造函数调用都应该是在常数表达式(5.19(中允许。

double是一个文字类型,文字类型的数组也是如此。这意味着代码中的v[0]v[1]实际上是常量表达式。

struct Parameters {
  static constexpr int n = 2;
  static constexpr double v[n] = {4.0, 5.0};
};
int main() {
  constexpr int a = Parameters::v[0];
  return 0;
}

gcc 4.8.2上的这段代码编译成以下代码:

0000000000000000 <main>:
   0:   55                      push   rbp
   1:   48 89 e5                mov    rbp,rsp
   4:   c7 45 fc 04 00 00 00    mov    DWORD PTR [rbp-0x4],0x4
   b:   b8 00 00 00 00          mov    eax,0x0
  10:   5d                      pop    rbp
  11:   c3                      ret 

是的,它是一个编译时常数。

clang 3.4产生类似的代码:

0000000000000000 <main>:
   0:   55                      push   rbp
   1:   48 89 e5                mov    rbp,rsp
   4:   b8 00 00 00 00          mov    eax,0x0
   9:   c7 45 fc 00 00 00 00    mov    DWORD PTR [rbp-0x4],0x0
  10:   c7 45 f8 04 00 00 00    mov    DWORD PTR [rbp-0x8],0x4
  17:   5d                      pop    rbp
  18:   c3                      ret

同样,它是一个编译时常数。

所有内容都是用-O0编译的。

附言:如果a被声明为const,那么对于gcc,除了clang之外什么都没有改变,值4不会像编译时常量一样直接被mov'ed。

如果既没有声明const也没有声明constexpr,则两个编译器都无法将Parameters::v[0]视为编译时常数。

struct Parameters {
   static constexpr int n = 2;
   static constexpr double v[n] = {4.0, 5.0};
};

据我所知,这个片段本身当然是合法的。C++11标准的7.1.5节[dcl.constexpr]说

constexpr说明符应仅应用于…文字类型的静态数据成员的声明

并且文字类型在3.9:中定义

如果类型是:,则该类型为文字类型

--标量类型;或

--文字类型的数组

因此,就我所知,static constexpr double v[2] = { ... }当然是有效的。

至于数组的成员是否为constexpr。。。我不确定。如果我们申报

constexpr double d = Parameter::v[1];

然后g++和clang都可以编译它,但clang版本无法链接到未定义的Parameters::v引用。我不知道这是否指向Clang错误,或者该构造是否无效。