c++ - 为什么不能在 C++11 中初始化静态数据成员?
问题描述
借助 c++11 的新特性,我们可以进行类内成员初始化。但是仍然不能在类中定义静态数据成员。
class A
{
static const int i = 10;
int j = 10;
const int k = 20;
static int m = 10; // error: non-const static data member must be initialized out of line
};
为什么不提供此功能?
解决方案
非静态数据成员类内初始化
首先,这与静态成员初始化完全不同。
类内成员初始化只是转换为构造函数初始化列表的语法糖。
例如
struct X
{
int a_ = 24;
int b_ = 11;
int c_;
X(int c) : c_{c}
{
}
X(int b, int c) : b_{b}, c_{c}
{
}
};
几乎相当于:
struct X
{
int a_;
int b_;
int c_;
X(int c) : a_{24}, b{11}, c_{c}
{
}
X(int b, int c) : a{24}, b_{b}, c_{c}
{
}
};
只是语法糖。在 C++11 之前,没有什么是用更冗长的代码做不到的。
静态数据成员类内初始化
这里的事情更复杂,因为静态数据成员必须只有 1 个符号。您应该阅读ODR(一个定义规则)。
让我们从 const 静态数据成员开始。您可能会惊讶于仅允许从编译时常量表达式进行初始化:
auto foo() { return 24; }
constexpr auto bar() { return 24 };
struct X
{
static const int a = foo(); // Error
static const int b = bar(); // Ok
};
实际规则(本身不是规则,但如果你愿意,可以推理)更一般(对于 const 和非常量静态数据成员):静态数据成员初始化,如果符合,必须是编译时表达式。这实际上意味着行初始化中唯一允许的静态数据成员是具有 constexpr 初始化的 const 静态数据成员。
现在让我们看看这背后的原因:如果你有一个行内初始化,这将使它成为一个定义,这意味着X
出现定义的每个编译单元都会有一个X::a
符号。每个这样的编译单元都需要初始化静态成员。在我们的示例foo
中,将为每个直接或间接包含定义为X
.
第一个问题是它出乎意料。调用的次数foo
将取决于包含的编译单元的数量X
,即使您foo
为单个静态成员的单个初始化编写了单个调用。
但是还有一个更严重的问题:foo
不是一个constexpr
函数,没有什么能阻止foo
在每次调用时返回不同的结果。所以你最终会得到一堆X::a
符号,它们应该在 ODR 下,但每个符号都用不同的值初始化。
如果您仍然不相信,那么还有第三个问题:拥有多个定义X::a
只会违反 ODR。所以……前两个问题只是 ODR 存在的一些动机。
强制定义 forX::a
是允许X::a
在单个编译单元中正确定义和初始化 : 的唯一方法。您仍然可以在标头中搞砸并编写行外定义和初始化,但是通过行内初始化,您肯定有多个初始化。
正如 nm 自 C++17 以来所显示的,您有inline
数据成员,在这里我们可以进行类内初始化:
struct X
{
static inline int i = foo();
};
现在我们可以理解为什么了:inline
编译器只会选择X::i
(来自一个编译单元)的一种定义,因此您只需对从一个编译单元中选择的初始化表达式进行一次评估。请注意,尊重 ODR 仍然是您的责任。
推荐阅读
- google-cloud-dataprep - 将云数据准备迁移到其他帐户
- r - 在 R 中为 SQL Server R 默认加载的包
- amazon-web-services - AWS - 如何限制用户删除或修改他人创建的 lambda 函数
- typescript - AngularFire2 的类型安全使用
- batch-file - 替换没有大写和空格的csv中的文本
- dart - 如何在 Flutter 中将 RefreshIndicator 与 FutureBuilder 一起使用?
- java - 无法在 Android WebView 中设置 HttpURLConnection 标头
- python - “NoSuchWindowException:没有这样的窗口:窗口已经关闭”,同时通过 Python3 使用 Selenium 和 WebDriver 切换选项卡
- excel - 使用 VBA 函数检查列是否存在 NA 错误并创建 IF-Then Else 输出
- python - 直接将python字符串转换为字节而不使用eval()