我想在我的类中有一个静态const char数组。GCC抱怨并告诉我应该使用constexpr,尽管现在它告诉我它是一个未定义的引用。如果我把数组设为非成员,它就会编译。这是怎么回事?

// .hpp
struct foo {
  void bar();
  static constexpr char baz[] = "quz";
};

// .cpp
void foo::bar() {
  std::string str(baz); // undefined reference to baz
}

当前回答

这实际上是c++ 11中的一个缺陷——正如其他人解释的那样,在c++ 11中,静态的constexpr成员变量与其他类型的constexpr全局变量不同,它具有外部链接,因此必须在某处显式地定义。

同样值得注意的是,在使用优化进行编译时,您通常可以在实践中使用没有定义的静态constexpr成员变量,因为它们可能在所有使用中都内联,但如果您在没有优化的情况下编译,您的程序通常会链接失败。这使得这成为一个非常常见的隐藏陷阱——您的程序在优化下编译良好,但一旦您关闭优化(可能是为了调试),它就无法链接。

好消息是——这个缺陷在c++ 17中被修复了!这种方法有点复杂:在c++ 17中,静态constexpr成员变量是隐式内联的。将内联应用于变量是c++ 17中的一个新概念,但这实际上意味着它们不需要在任何地方显式定义。

其他回答

更优雅的解决方案不是将char[]更改为:

static constexpr char * baz = "quz";

这样我们就可以在一行代码中包含定义/声明/初始化式。

在我的环境中,gcc版本是5.4.0。添加“-O2”可以修复此编译错误。在要求优化时,gcc似乎可以处理这种情况。

添加到您的cpp文件:

constexpr char foo::baz[];

原因:你必须提供静态成员的定义和声明。声明和初始化式放在类定义中,但成员定义必须分开。

这实际上是c++ 11中的一个缺陷——正如其他人解释的那样,在c++ 11中,静态的constexpr成员变量与其他类型的constexpr全局变量不同,它具有外部链接,因此必须在某处显式地定义。

同样值得注意的是,在使用优化进行编译时,您通常可以在实践中使用没有定义的静态constexpr成员变量,因为它们可能在所有使用中都内联,但如果您在没有优化的情况下编译,您的程序通常会链接失败。这使得这成为一个非常常见的隐藏陷阱——您的程序在优化下编译良好,但一旦您关闭优化(可能是为了调试),它就无法链接。

好消息是——这个缺陷在c++ 17中被修复了!这种方法有点复杂:在c++ 17中,静态constexpr成员变量是隐式内联的。将内联应用于变量是c++ 17中的一个新概念,但这实际上意味着它们不需要在任何地方显式定义。

对于静态成员的外部链接,我的解决方法是使用constexpr引用成员getter(这不会遇到@gnzlbg作为来自@deddebme的答案的注释提出的问题)。 这个习惯用法对我来说很重要,因为我讨厌在我的项目中有多个.cpp文件,并试图将数量限制为一个,其中除了#includes和一个main()函数外什么都没有。

// foo.hpp
struct foo {
  static constexpr auto& baz() { return "quz"; }
};

// some.cpp

  auto sz = sizeof(foo::baz()); // sz == 4

  auto& foo_baz = foo::baz();  // note auto& not auto
  auto sz2 =  sizeof(foo_baz);    // 4
  auto name = typeid(foo_baz).name();  // something like 'char const[4]'