我正在嘗試將舊的 C 03 代碼庫遷移到 C 11。但我不明白 gcc 在以下情況下警告我什么:
% g -std=c 03 t.cxx
% g -std=c 11 t.cxx
t.cxx: In function ‘int main()’:
t.cxx:8:21: warning: converting to ‘A’ from initializer list would use explicit constructor ‘A::A(int)’
8 | int main() { B b = {}; }
| ^
t.cxx:8:21: note: in C 11 and above a default constructor can be explicit
struct A {
explicit A(int i = 42) {}
};
struct B {
A a;
};
int main() {
B b = {};
return 0;
}
我在這里要做的只是一個基本的零初始化。對于 C 03 似乎是合法的,但我不明白如何在 C 11 中表達等價物。
作為參考,我正在使用:
% g --version
g (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
uj5u.com熱心網友回復:
由于下面解釋的原因,給定的程式格式錯誤。
C 20
B是一個聚合。由于您沒有顯式初始化a,因此 dcl.init.aggr#5適用:
- 對于非聯合聚合,不是顯式初始化元素的每個元素都按如下方式初始化:
5.2 否則,如果元素不是參考,則從空的初始化串列([dcl.init.list])復制初始化元素。
這意味著它a是從一個空的初始化串列中復制初始化的。換句話說,就好像我們在寫:
A a = {}; // not valid see reason below
另請注意,復制初始化背景關系中的串列初始化稱為復制串列初始化,這里就是這種情況。從over.match.list#1.2:
在復制串列初始化中,如果選擇了顯式建構式,則初始化為ill-formed。
從本質上講,失敗的原因A a = {};是格式不正確。
C 11
因為B是一個聚合,并且串列中的初始化子句比聚合中的成員少,并且來自聚合初始化檔案:
聚合初始化的效果是:
- 如果初始化器子句的數量小于成員和基數 (C 17 起) 或初始化器串列完全為空,則剩余的成員和基數 (C 17 起)由它們的默認成員初始化器初始化,如果在類定義中提供,否則(自 C 14 起)根據通常的串列初始化規則從空串列復制初始化(該規則使用默認建構式對非型別別和非聚合類執行值初始化,以及聚合的聚合初始化)。
這再次意味著它a是從一個空串列中初始化的副本{}。也就是說,就好像我們在寫:
A a = {}; // not valid see reason below
但是從over.match.funcs:
在復制串列初始化中,如果選擇了顯式建構式,則初始化為ill-formed。
所以我們再次面臨同樣的問題,即A a = {};無效。
解決方案
為了解決這個問題,我們可以在串列中傳遞A{}orA{0}作為初始化器,如下所示:
B b = { A{} }; //ok now
B c = { A{0} }; //also ok
作業演示。
筆記
請注意,A a{};另一方面,寫作是格式良好的,因為這是一個直接初始化背景關系,所以它是直接串列初始化。
uj5u.com熱心網友回復:
由于B是一個聚合類,因此 C 11 草案標準中的以下規則適用[decl.init.aggr/7]:
如果串列中的初始化子句少于聚合中的成員,則每個未顯式初始化的成員都應從空的初始化器串列([dcl.init.list])中初始化。
b.a因此從一個空的初始化串列初始化。但這可以在兩種不同的背景關系中執行[dcl.init.list/1]:
串列初始化可以發生在直接初始化或復制初始化背景關系中;直接初始化背景關系中的串列初始化稱為直接串列初始化,而復制初始化背景關系中的串列初始化稱為復制串列初始化。
在直接初始化背景關系中,一切都很好A a{};。但是,在復制初始化背景關系中,它不是,因為A a={};不是。
GCC 和 Clang 似乎在這里應用了復制初始化背景關系,而 MSVC 似乎應用了直接初始化背景關系(至少,它不會發出任何警告或錯誤)。
現場演示:https ://godbolt.org/z/1n66v1n5c
我不確定哪個編譯器是正確的,或者是否甚至指定了應該在此處應用哪個背景關系。由于b是復制串列初始化,復制初始化背景關系對我來說更有意義。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/495875.html
上一篇:將非動態分配的物件推送到指標向量
