【发布时间】:2013-09-25 10:03:29
【问题描述】:
Stack Overflow 上有几个问题,类似于“为什么我不能在 C++ 中初始化静态数据成员”。大多数答案都引用标准告诉你什么你可以做什么;那些试图回答为什么的人通常指向一个链接(现在看起来不可用)[编辑:实际上它是可用的,见下文]在 Stroustrup 的网站上,他声明允许静态成员的类内初始化会违反单一定义规则 (ODR)。
但是,这些答案似乎过于简单化。编译器完全能够在需要时解决 ODR 问题。例如,考虑 C++ 标头中的以下内容:
struct SimpleExample
{
static const std::string str;
};
// This must appear in exactly one TU, not a header, or else violate the ODR
// const std::string SimpleExample::str = "String 1";
template <int I>
struct TemplateExample
{
static const std::string str;
};
// But this is fine in a header
template <int I>
const std::string TemplateExample<I>::str = "String 2";
如果我在多个翻译单元中实例化TemplateExample<0>,编译器/链接器的魔法就会发挥作用,我会在最终的可执行文件中得到一份TemplateExample<0>::str 的副本。
所以我的问题是,鉴于编译器显然可以解决模板类的静态成员的 ODR 问题,为什么它不能对非模板类也这样做?
编辑:可以使用 Stroustrup 常见问题解答 here。相关句子是:
但是,为了避免复杂的链接器规则,C++ 要求每个对象都有唯一的定义。如果 C++ 允许在类内定义需要作为对象存储在内存中的实体,那么这条规则就会被打破
然而,那些“复杂的链接器规则”似乎确实存在并且在模板案例中使用,那么为什么不在简单案例中呢?
【问题讨论】:
-
C++11 放宽了这个限制。您可以使用常量表达式进行类内初始化。
-
是的,但我的理解是,即便如此,它也需要一个定义(没有初始化器)出现在命名空间范围内的一个翻译单元中;但是在模板情况下,它可能出现在标题中,因此出现在多个 TU 中。我的问题是为什么模板案例中使用的符号合并魔法也不能用于普通的非模板类。
-
模板不是原始 C++ 语言的一部分。
-
C++17 允许内联初始化静态数据成员(即使对于非整数类型):
inline static int x[] = {1, 2, 3};。请参阅 en.cppreference.com/w/cpp/language/static#Static_data_members
标签: c++ static-members language-lawyer one-definition-rule in-class-initialization