C++ 为什么可以';在类中初始化'const std::string'作为静态成员

C++ 为什么可以';在类中初始化'const std::string'作为静态成员,c++,c++11,in-class-initialization,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'; } #包括 #包括 甲级{ 公众

我有以下工作代码:

#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';
}
#包括
#包括
甲级{
公众:
const std::string test=“42”;
//静态常量std::string test=“42”//失败
};
内部主(空){
A A;

std::cout你的问题有两部分。标准是怎么说的?为什么

>类型的静态成员:const STD::String ,它需要在类说明符之外定义,并且在一个翻译单元中有一个定义。这是一个定义规则的一部分,并且在C++标准的第3条中指定。 但是为什么呢

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


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

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


你可能会争辩说,编译器应该在使用对象的每个翻译单元中存储关于具有静态存储持续时间的对象的信息,然后链接器应该在链接时将这些定义合并到程序映像中的一个对象中。我猜为什么不这样做,是因为它需要链接器提供太多的智能r、

当涉及到静态时,您只能在声明点初始化
const
整数类型和枚举。@juanchopanza我知道。我认为在c++11中它可以被克服。我认为您在3中指出了一个问题。是的,这是可行的,但需要将这种情况作为特殊情况,否则链接器只是不“喜欢”多个定义。我从一开始就不理解这些限制的基本原理。多个定义的相同符号的问题也适用于模板实例化,因此链接器已经知道如何处理它(通过丢弃除一个之外的所有副本)@ondav如果它的
常量static
真的是“多个”.不仅仅是副本吗?为什么不选一个呢?我对链接器输入、地址翻译和其他东西不太熟悉。也许有人知道为什么这么难或放弃了这个想法。老实说,以前我认为是类内初始化的问题多于多个符号,但似乎不是这样。常量积分不需要存储的原因是,它被编译器用作常量表达式,并在使用点内联。它永远不会进入程序映像。“除了我可以编写需要存储的
&const_int_member
。而我从不引用的静态常量int不需要存储。最后,模板要求链接器提供完全相同的“太多智能”。就我所知,整个参数在每个细节上都是毫无意义的。(并不是只有你一个人这样做…@Nemo:执行间接寻址(
&const_int_member
)会导致它成为odr使用,因此9.4.2p4中提到的要求开始发挥作用,即“如果使用odr,则仍应在名称空间范围中定义该成员(3.2)”在程序和命名空间范围定义中不应包含初始值设定项。”(即,现在需要一个定义)对于模板,它们有特殊的实例化单元,在转换阶段8中使用特殊规则。确定,但只要您将其设置为“odr used”,它与任何用户定义的类型都有相同的问题。因此,基本原理仍然存在分歧;没有理由不让任何类型在标头中初始化,即使您仍然需要命名空间范围中的某个位置的单个定义。这就是“const int”和“const where”之间的区别就我所知,当你仔细思考时,真的没有任何意义…@Nemo:不,它没有相同的问题。正如我之前所说的,常量整数类型可以内联(无论是否使用odr,以及它们是否有存储),而用户定义的类型不能在所有情况下都是,因为它们需要动态初始化和存储。因此,设计漂亮的用户定义文本很可能没有解决方法。我已经知道,integral是一个非常特殊的例外,只是因为它将内联到程序集调用中,并且不需要在最终对象中存储。因此你不能取消引用它,如果它是在类外定义的,你可以取消引用。我认为其他答案甚至有例子。我也知道9.4.2。我只是认为在c++11中,它们以某种方式使之成为可能。我的问题现在似乎有点多余。如果我不接受这一点,也许有人会解释为什么编译器不能执行看似简单的工作。