简体   繁体   中英

Are constexpr array members compile time constants?

Is the code fragment

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

legal C++11? And, if so, are Parameters::v[0] and Parameters::v[1] compile time constants or is just the pointer Parameters::v itself a constexpr (whatever that would mean at compile time)?

As you can see I am generally a bit confused about constexpr arrays and their initialization in classes/structs. Please feel free to not only answer my specific question but also to mention common pitfalls and the like concerning this topic.

I see no problem with the construct. Quoting C++11, [dcl.constexpr] :

§1 The constexpr specifier shall be applied only to the definition of a variable, the declaration of a function or function template, or the declaration of a static data member of a literal type (3.9). ...

§9 A constexpr specifier used in an object declaration declares the object as const . Such an object shall have literal type and shall be initialized. If it is initialized by a constructor call, that call shall be a constant expression (5.19). Otherwise, or if a constexpr specifier is used in a reference declaration, every full-expression that appears in its initializer shall be a constant expression. Each implicit conversion used in converting the initializer expressions and each constructor call used for the initialization shall be one of those allowed in a constant expression (5.19).

double is a literal type, and so is an array of literal types. Which means that v[0] and v[1] from your code are indeed constant expressions.

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;
}

This code on gcc 4.8.2 compiles into the following:

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 

So yes, it is a compile time constant.

clang 3.4 produces similar code:

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

Again, it is a compile time constant.

Everything was compiled with -O0.

PS: If a is declared const, then for gcc nothing changes but for clang does, the value 4 is not mov'ed directly as if was a compile time constant.

If a is declared neither const or constexpr then both compilers fail to treat Parameters::v[0] as a compile time constant.

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

This fragment by itself is certainly legal as far as I can tell. Section 7.1.5 [dcl.constexpr] of the C++11 standard says that

The constexpr specifier shall be applied only to... the declaration of a static data member of a literal type

and a literal type is defined in 3.9:

A type is a literal type if it is:

— a scalar type; or...

— an array of literal type

So static constexpr double v[2] = { ... } is certainly valid as far as I can tell.

As to whether the members of the array are constexpr ... I'm not sure. If we declare

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

then both g++ and clang compile it okay, but the clang version fails to link with an undefined reference to Parameters::v . I don't know whether this points to a Clang bug, or whether the construct is invalid.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM