C++11 允许对非静态和非常量成员进行类内初始化.发生了什么变化?
在 C++11 之前,我们只能对整型或枚举类型的静态 const 成员执行类内初始化.Stroustrup 在他的 C++ FAQ 中讨论了这个问题,并给出了以下示例:
Before C++11, we could only perform in-class initialization on static const members of integral or enumeration type. Stroustrup discusses this in his C++ FAQ, giving the following example:
class Y {
const int c3 = 7; // error: not static
static int c4 = 7; // error: not const
static const float c5 = 7; // error: not integral
};
还有以下推理:
那么为什么会存在这些不方便的限制呢?一个类通常在头文件中声明,并且头文件通常包含在许多翻译单元中.但是,为了避免复杂的链接器规则,C++ 要求每个对象都有唯一的定义.如果 C++ 允许将需要作为对象存储在内存中的实体在类内定义,则该规则将被打破.
So why do these inconvenient restrictions exist? A class is typically declared in a header file and a header file is typically included into many translation units. However, to avoid complicated linker rules, C++ requires that every object has a unique definition. That rule would be broken if C++ allowed in-class definition of entities that needed to be stored in memory as objects.
但是,C++11 放宽了这些限制,允许对非静态成员进行类内初始化(第 12.6.2/8 节):
However, C++11 relaxes these restrictions, allowing in-class initialization of non-static members (§12.6.2/8):
在非委托构造函数中,如果给定的非静态数据成员或基类没有由 mem-initializer-id 指定(包括没有 mem-initializer-list 因为构造函数没有ctor-initializer)并且实体不是抽象类(10.4)的虚拟基类,那么
In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then
- 如果实体是具有 brace-or-equal-initializer 的非静态数据成员,则实体按照 8.5 中的规定进行初始化;
- 否则,如果实体是变体成员 (9.5),则不执行初始化;
- 否则,实体会被默认初始化 (8.5).
- if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized as specified in 8.5;
- otherwise, if the entity is a variant member (9.5), no initialization is performed;
- otherwise, the entity is default-initialized (8.5).
如果使用 constexpr
说明符标记非常量静态成员,第 9.4.2 节还允许在类内初始化它们.
Section 9.4.2 also allows in-class initialization of non-const static members if they are marked with the constexpr
specifier.
那么我们在 C++03 中受到限制的原因是什么?我们只是简单地接受复杂的链接器规则"还是进行了其他更改以使其更易于实施?
So what happened to the reasons for the restrictions we had in C++03? Do we just simply accept the "complicated linker rules" or has something else changed that makes this easier to implement?
推荐答案
简短的回答是他们保持链接器大致相同,但代价是编译器比以前更复杂.
The short answer is that they kept the linker about the same, at the expense of making the compiler still more complicated than previously.
也就是说,这不会导致链接器需要对多个定义进行排序,而是仍然只导致一个定义,并且编译器必须对其进行排序.
I.e., instead of this resulting in multiple definitions for the linker to sort out, it still only results in one definition, and the compiler has to sort it out.
这也导致程序员需要整理一些更复杂的规则,但它大多很简单,没什么大不了的.当您为单个成员指定了两个不同的初始化程序时,就会出现额外的规则:
It also leads to somewhat more complex rules for the programmer to keep sorted out as well, but it's mostly simple enough that it's not a big deal. The extra rules come in when you have two different initializers specified for a single member:
class X {
int a = 1234;
public:
X() = default;
X(int z) : a(z) {}
};
现在,此时的额外规则处理使用非默认构造函数时用于初始化 a
的值.答案很简单:如果您使用的构造函数没有指定任何其他值,那么 1234
将用于初始化 a
-- 但如果您使用指定其他值的构造函数,则 1234
基本上会被忽略.
Now, the extra rules at this point deal with what value is used to initialize a
when you use the non-default constructor. The answer to that is fairly simple: if you use a constructor that doesn't specify any other value, then the 1234
would be used to initialize a
-- but if you use a constructor that specifies some other value, then the 1234
is basically ignored.
例如:
#include <iostream>
class X {
int a = 1234;
public:
X() = default;
X(int z) : a(z) {}
friend std::ostream &operator<<(std::ostream &os, X const &x) {
return os << x.a;
}
};
int main() {
X x;
X y{5678};
std::cout << x << "
" << y;
return 0;
}
结果:
1234
5678
相关文章