C++ constexpr静态成员变量的奇怪行为

C++ constexpr静态成员变量的奇怪行为,c++,c++11,C++,C++11,这是我们的后续问题 以下程序构建并运行良好 #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; } 我在g++4.8.2中遇到

这是我们的后续问题

以下程序构建并运行良好

#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;
}
我在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 在类定义之外


但是,我不清楚为什么使用阵列的一个成员不会导致链接器错误,而使用阵列会导致链接器错误。

标准不要求对未能提供所需定义的故障进行任何诊断

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

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

这意味着允许实现优化对这些变量的访问,这就是GCC的第一种情况

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

但无论哪种方式,该程序都是一个错误,因为即使是
A::dict[0]
也是一个ODR用法:

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

3变量
x
的名称显示为可能计算的表达式
ex
ex
使用的odr,除非将左值到右值的转换(4.1)应用到
x
生成一个常量表达式(5.19),该表达式不会调用任何非平凡函数,并且如果
x
是一个对象,
ex
是表达式
e
的潜在结果集的一个元素,其中左值到右值的转换(4.1)应用于
e
,或者
e
是一个废弃值表达式(第5条)。[……]


A::dict
的使用不涉及左值到右值的转换,它涉及数组到指针的转换,因此例外情况不适用。

除了@hvd提供的信息之外

<>从C++草案标准N33 37(强调矿山):

9.4.2静态数据成员

3如果非易失性
const static
数据成员是整型或枚举型,则其在类定义中的声明可以指定一个大括号或相等的初始值设定项,其中作为赋值表达式的每个初始值设定项子句都是常量表达式(5.19)。可以使用
constepr
说明符在类定义中声明文本类型的
static
数据成员;如果是这样,其声明应指定一个大括号或相等的初始值设定项,其中作为赋值表达式的每个初始值设定项子句都是常量表达式。[注意:在这两种情况下,成员可能出现在常量表达式中。-结束注意]如果在程序中使用odr(3.2),则该成员仍应在名称空间范围中定义,并且名称空间范围定义不应包含初始值设定项。


鉴于
A::data
是表达式
A::data[0]
中使用的odr,根据标准,它应在名称空间范围内定义。g++能够成功地创建一个程序,而无需在namescpace作用域中定义
a::data
,这一事实并不能使程序正常运行。为了符合标准,应该定义
A::data

因为前者需要一个值,而后者需要一个地址?我认为两者都是odr的用途。在
dict[0]
case.clang也不喜欢你的第一个版本,给出了与你的第二个版本相同的未定义的参考信息。看起来g++在幕后做了一些不应该做的有趣的事情。这可能得益于关于为什么
a::dict[0]的讨论
是odr的一种用法。@T.C.它不再是了。@Columbo感谢您的更新。我正要编辑我的答案,但随后注意到问题被标记为C++11,因此信息仍然正确。:)嗯,你说得对,也有。这并不是说“不需要诊断”,所以您甚至可以合理地争辩说,GCC由于未能诊断违反该规则的行为而存在缺陷。(但更可能的是,这在措辞上是一个nit,诊断不是必需的。)@hvd,我认为“无需诊断”仍然适用,因为它引用了“使用的odr(3.2)”,是的,但使用的odr只是指[3.2]p3,它指定了odr使用的变量,而不是[3.2]p4,这需要odr使用的变量的定义。[3.2]p3是“odr使用”以斜体显示的地方,斜体显示意味着它定义了术语“odr使用”的含义。 /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[];