【问题标题】:Are constexpr array members compile time constants?constexpr 数组成员是否编译时间常数?
【发布时间】:2014-05-01 17:46:40
【问题描述】:

是代码片段

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++ c++11


【解决方案1】:

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

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

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

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

【讨论】:

    【解决方案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;
    }
    

    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 编译的。

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

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

    【讨论】:

      【解决方案3】:
      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 错误,或者构造是否无效。

      【讨论】:

      • 从技术上讲,我认为数组的成员不能是constexpr,因为它们不是完整的对象。但是无论如何您都不能直接访问它们,您只能通过表达式Parameters::v[0](或1)访问它们。这满足了常量表达式的所有要求。
      • @Angew 但是从实际的角度来看,Parameters::v 的地址将由链接器或运行时设置,编译器不知道。这么天真,*(Parameters::v + 1) 作为constexpr 会有点棘手?
      • C++11[expr.const]§2,3 定义常量表达式的reuqirements,并且数组解引用不违反其中任何一个。所以它是一个常量表达式。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-25
      • 2022-01-03
      • 2020-03-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多