为什么我不能在类中初始化`const const std :: string`一个静态成员

时间:2013-06-27 19:42:31

标签: c++ c++11 in-class-initialization

我有以下工作代码:

#include <string>
#include <iostream>

class A {
public:
  const std::string test = "42";
  //static const std::string test = "42"; // fails
};

int main(void){
  A a;
  std::cout << a.test << '\n';
}

为什么不能让测试成为static const有充分理由吗?我确实在c ++ 11之前理解它受到标准的限制。我认为c ++ 11引入了类内初始化,使其更友好一些。很长一段时间以来,我也没有这种语义可用于整数类型。

当然,它适用于const std::string A::test = "42";

形式的课外初始化

我想,如果你能把它变成非静态的,那么问题在于两者中的一个。初始化它的类外范围(通常const s是在对象实例化期间创建的)。但是,如果您创建一个独立于该类的任何其他成员的对象,我认为这不是问题。第二个是静态成员的多个定义。例如。如果它被包含在几个.cpp文件中,登陆到几个目标文件,然后链接器在将这些对象链接在一起(例如,到一个可执行文件中)时会遇到麻烦,因为它们将包含相同符号的副本。据我所知,这完全等同于在标题中的类声明下提供类外权利的情况,然后在多个地方包含这个公共标题。我记得,这会导致链接器错误。

但是,现在处理此问题的责任已移至用户/程序员。如果想要一个带有static的库,他们需要提供一个类外定义,将它编译成一个单独的目标文件,然后将所有其他对象链接到这个,因此只有一个副本符号的二进制定义。

我在Do we still need to separately define static members, even if they are initialised inside the class definition?Why can't I initialize non-const static member or static array in class?中阅读了答案。

我仍然想知道:

  1. 这只是一个标准的事情,还是背后有更深层次的推理?
  2. 可以使用constexpr和用户定义来解决这个问题 文字机制。 clang和g ++都说变量不能有非文字类型。也许我可以做一个。 (也许出于某种原因,这也是一个坏主意)
  3. 链接器只包含一个副本是否真的是一个大问题 符号?因为它是static const所有都应该是二元精确的 不可变的副本。
  4. 如果我遗失或误解了某些内容,请发表评论。

1 个答案:

答案 0 :(得分:5)

你的问题有两个部分。标准说什么?为什么会这样呢?

对于类型为const std::string的静态成员,需要在类说明符之外定义它,并在其中一个转换单元中定义一个。这是One Definition Rule的一部分,在C ++标准的第3节中有详细说明。

但为什么?

问题是具有静态存储持续时间的对象在最终程序映像中需要唯一的静态存储,因此需要从一个特定的转换单元链接。类说明符在一个翻译单元中没有home,它只是定义了类型(需要在使用它的所有翻译单元中进行相同的定义)。

常量积分不需要存储的原因是它被编译器用作常量表达式并在使用点内联。它永远不会成为程序图像。

但是,具有静态存储持续时间的复杂类型(如std::string)需要存储,即使它们是const。这是因为它们可能需要动态初始化(在进入main之前调用它们的构造函数)。

您可能认为编译器应该在每个使用它们的转换单元中存储有静态存储持续时间的对象的信息,然后链接器应该在链接时将这些定义合并到程序映像中的一个对象中。我猜测为什么没有这样做,是因为它需要来自链接器的太多智能。