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

时间:2023-01-06 22:57:01

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)?

合法的C ++ 11?并且,如果是这样,是Parameters :: v [0]和Parameters :: v [1]编译时间常量或者只是指针Parameters :: v本身是constexpr(无论编译时是什么意思)?

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.

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

3 个解决方案

#1


4  

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

我发现构造没有问题。引用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). ...

§1constexpr说明符应仅适用于变量的定义,函数或函数模板的声明,或文字类型的静态数据成员的声明(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).

§9对象声明中使用的constexpr说明符将对象声明为const。这样的对象应具有文字类型并应初始化。如果它是由构造函数调用初始化的,那么该调用应该是一个常量表达式(5.19)。否则,或者如果在引用声明中使用constexpr说明符,则其初始值设定项中出现的每个完整表达式都应为常量表达式。用于转换初始化表达式和用于初始化的每个构造函数调用的每个隐式转换都应该是常量表达式中允许的转换之一(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.

double是文字类型,文字类型也是如此。这意味着代码中的v [0]和v [1]确实是常量表达式。

#2


2  

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

就我所知,这个片段本身就是合法的。 C ++ 11标准的第7.1.5节[dcl.constexpr]说明了这一点

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

constexpr说明符只能应用于......文字类型的静态数据成员的声明

and a literal type is defined in 3.9:

并且在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.

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

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

至于阵列的成员是否是constexpr ......我不确定。如果我们宣布

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.

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

#3


2  

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:

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 

So yes, it is a compile time constant.

所以是的,它是一个编译时常量。

clang 3.4 produces similar code:

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

Again, it is a compile time constant.

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

Everything was compiled with -O0.

一切都是用-O0编译的。

P.S.: 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.

P.S。:如果a被声明为const,那么对于gcc没什么变化,但是对于clang来说,值4不会直接移动,就好像是编译时常量一样。

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

如果声明a既不是const也不是constexpr,则两个编译器都不能将Parameters :: v [0]视为编译时常量。

#1


4  

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

我发现构造没有问题。引用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). ...

§1constexpr说明符应仅适用于变量的定义,函数或函数模板的声明,或文字类型的静态数据成员的声明(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).

§9对象声明中使用的constexpr说明符将对象声明为const。这样的对象应具有文字类型并应初始化。如果它是由构造函数调用初始化的,那么该调用应该是一个常量表达式(5.19)。否则,或者如果在引用声明中使用constexpr说明符,则其初始值设定项中出现的每个完整表达式都应为常量表达式。用于转换初始化表达式和用于初始化的每个构造函数调用的每个隐式转换都应该是常量表达式中允许的转换之一(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.

double是文字类型,文字类型也是如此。这意味着代码中的v [0]和v [1]确实是常量表达式。

#2


2  

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

就我所知,这个片段本身就是合法的。 C ++ 11标准的第7.1.5节[dcl.constexpr]说明了这一点

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

constexpr说明符只能应用于......文字类型的静态数据成员的声明

and a literal type is defined in 3.9:

并且在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.

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

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

至于阵列的成员是否是constexpr ......我不确定。如果我们宣布

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.

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

#3


2  

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:

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 

So yes, it is a compile time constant.

所以是的,它是一个编译时常量。

clang 3.4 produces similar code:

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

Again, it is a compile time constant.

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

Everything was compiled with -O0.

一切都是用-O0编译的。

P.S.: 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.

P.S。:如果a被声明为const,那么对于gcc没什么变化,但是对于clang来说,值4不会直接移动,就好像是编译时常量一样。

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

如果声明a既不是const也不是constexpr,则两个编译器都不能将Parameters :: v [0]视为编译时常量。