問題描述
在 C++11 之前,我們只能對整型或枚舉類型的靜態常量成員執行類內初始化.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
- 如果實體是具有大括號或相等初始化器的非靜態數據成員,則實體按照 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).
第 9.4.2 節還允許對非常量靜態成員進行類內初始化,前提是它們用 constexpr
說明符標記.
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
這篇關于C++11 允許非靜態和非常量成員的類內初始化.發生了什么變化?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!