使用constexpr静态成员变量的奇怪行为

时间:2015-02-15 20:09:15

标签: c++ c++11

这是Undefined reference to static constexpr char[][]的后续问题。

以下程序构建并运行良好。

#include <iostream>

struct A {
   constexpr static char dict[] = "test";

   void print() {
      std::cout << A::dict[0] << std::endl;
   }
};

int main() {
   A a;
   a.print();
   return 0;
}

但是,如果我将A::print()更改为:

   void print() {
      std::cout << A::dict << std::endl;
   }

我在g ++ 4.8.2中收到以下链接器错误。

/tmp/cczmF84A.o: In function `A::print()':
socc.cc:(.text._ZN1A5printEv[_ZN1A5printEv]+0xd): undefined reference to `A::dict'
collect2: error: ld returned 1 exit status

可以通过添加一行来解决链接器错误:

constexpr char A::dict[];

在类定义之外。

但是,我不清楚为什么使用数组的一个成员在使用数组时不会导致链接器错误导致链接器错误。

2 个答案:

答案 0 :(得分:7)

如果无法提供需要的定义,则该标准不需要任何诊断。

  

3.2一个定义规则[basic.def.odr]

     

4每个程序应该只包含该程序中使用的每个非内联函数或变量的一个定义;无需诊断。 [...]

这意味着允许实现优化对这些变量的访问,以及在您的第一个GCC案例中发生的事情。

GCC和clang都决定他们更喜欢一致的用户体验,其中关于缺失定义的错误消息不依赖于优化级别。通常,这意味着任何缺少的定义都会导致错误消息。但是,在这种情况下,即使在-O0,GCC也会进行一些最小化优化,从而避免错误。

但是程序是错误的,因为即使A::dict[0]是ODR使用:

  

3.2一个定义规则[basic.def.odr]

     

3 x的名称显示为可能评估的表达式ex的变量exx使用,除非将{左值 - 右值转换(4.1)应用于{{} 1}}产生一个不调用任何非平凡函数的常量表达式(5.19),如果x是一个对象,ex是表达式{{e的潜在结果集的一个元素。 1}},其中左值到右值转换(4.1)应用于e,或e是丢弃值表达式(第5节)。 [...]

使用A::dict并不涉及左值到右值的转换,它涉及数组到指针的转换,因此异常不适用。

答案 1 :(得分:0)

除了@hvd in his answer提供的信息......

来自C ++ Draft Standard N3337(强调我的):

  

9.4.2静态数据成员

     

3如果非易失性const static数据成员是整数或枚举类型,则其在类定义中的声明可以指定大括号或等于初始值,其中每个< em> initializer-clause 是一个赋值表达式是一个常量表达式(5.19)。可以使用static说明符在类定义中声明文字类型的constexpr数据成员;如果是这样,它的声明应指定一个大括号或等于初始化,其中作为赋值表达式的每个 initializer-clause 是一个不断表达。 [注意:在这两种情况下,成员可能会出现在常量表达式中。 - 结束注释] 如果程序中使用了odr-used(3.2),并且命名空间范围定义不包含初始值设定项<,则仍应在命名空间作用域中定义该成员。 / em>的

鉴于A::data在表达式A::data[0]中使用了A::data,根据标准,它应在命名空间范围内定义。 g ++能够成功创建没有在名称空间范围内定义A::data的程序这一事实并不能使程序正确。为了符合标准,应定义{{1}}。