亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

C ++ 11允許非靜態和非const成員的類內初始化。發生了什么變化?

C ++ 11允許非靜態和非const成員的類內初始化。發生了什么變化?

C++
千萬里不及你 2019-11-11 13:11:40
在C ++ 11之前,我們只能對整數或枚舉類型的靜態const成員執行類內初始化。Stroustrup在他的C ++ FAQ中對此進行了討論,并給出了以下示例: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 ++允許對需要作為對象存儲在內存中的實體進行類內定義,則該規則將被打破。但是,C ++ 11放寬了這些限制,允許對非靜態成員進行類內初始化(第12.6.2 / 8節):在非委托構造函數中,如果給定的非靜態數據成員或基類未由mem-initializer-id指定(包括沒有mem-initializer-list的情況,因為構造函數沒有ctor-initializer)并且該實體不是抽象類(10.4)的虛擬基類,則如果實體是具有brace-or-equal-initializer的非靜態數據成員,則按照8.5中的指定進行初始化;否則,如果實體是變量成員(9.5),則不執行初始化。否則,該實體為默認初始化(8.5)。如果非常量成員用說明constexpr符標記,則9.4.2節還允許非常量成員的類內初始化。那么,我們在C ++ 03中受到限制的原因發生了什么?我們是否只是接受“復雜的鏈接器規則”,還是進行了其他更改以使其更容易實現?
查看完整描述

3 回答

?
牛魔王的故事

TA貢獻1830條經驗 獲得超3個贊

簡短的答案是,它們使鏈接程序保持相同,但代價是使編譯器比以前更加復雜。


即,除了導致鏈接器要定義多個定義之外,它仍然僅生成一個定義,而編譯器必須對其進行分類。


這也導致程序員要整理出一些更復雜的規則,但是它非常簡單,所以沒什么大不了的。當您為單個成員指定了兩個不同的初始化程序時,就會出現額外的規則:


class X { 

    int a = 1234;

public:

    X() = default;

    X(int z) : a(z) {}

};

現在,這時的額外規則處理a使用非默認構造函數時用于初始化的值。答案很簡單:如果使用未指定任何其他值的構造函數,1234則將使用初始化a-但是,如果使用指定其他值的構造函數,1234則基本上忽略。


例如:


#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 << "\n" << y;

    return 0;

}

結果:


1234

5678


查看完整回答
反對 回復 2019-11-11
?
交互式愛情

TA貢獻1712條經驗 獲得超3個贊

我猜想推理可能是在模板完成之前編寫的。對于C ++ 11支持模板的靜態成員而言,對于靜態成員的類內初始化器來說,所有必需的“復雜鏈接器規則”已經存在。


考慮


struct A { static int s = ::ComputeSomething(); }; // NOTE: This isn't even allowed,

                                                   // thanks @Kapil for pointing that out


// vs.


template <class T>

struct B { static int s; }


template <class T>

int B<T>::s = ::ComputeSomething();


// or


template <class T>

void Foo()

{

    static int s = ::ComputeSomething();

    s++;

    std::cout << s << "\n";

}

在所有三種情況下,編譯器的問題都是相同的:它應在哪個轉換單元中發出的定義s以及對其進行初始化所需的代碼?一種簡單的解決方案是將其發送到任何地方,然后讓鏈接程序對其進行整理。這就是鏈接器已經支持諸如之類的原因的原因__declspec(selectany)。沒有它,就不可能實現C ++ 03。這就是為什么沒有必要擴展鏈接器的原因。


坦率地說:我認為舊標準中的推理完全是錯誤的。


更新


正如Kapil指出的那樣,當前標準(C ++ 14)甚至不允許我的第一個示例。無論如何,我還是保留了它,因為IMO是實現過程中最困難的情況(編譯器,鏈接器)。我的觀點是:即使是這種情況,也沒有比例如使用模板時所允許的情況難。


查看完整回答
反對 回復 2019-11-11
?
慕的地10843

TA貢獻1785條經驗 獲得超8個贊

從理論上講,So why do these inconvenient restrictions exist?...原因是有效的,但可以輕松地繞開它,而這正是C ++ 11所做的。


當你有一個文件,它只是包括文件和忽略任何初始化。僅在實例化類時才初始化成員。


換句話說,初始化仍然與構造函數聯系在一起,只是表示法有所不同并且更加方便。如果未調用構造函數,則不會初始化值。


如果調用了構造函數,則使用類內初始化(如果存在)初始化值,否則構造函數可以使用自己的初始化覆蓋它們。初始化的路徑本質上是相同的,即通過構造函數。


從Stroustrup自己在C ++ 11上的常見問題中可以明顯看出這一點。


查看完整回答
反對 回復 2019-11-11
  • 3 回答
  • 0 關注
  • 411 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號