การเริ่มต้น C ++ เป็นศูนย์ - เหตุใด "b" ในโปรแกรมนี้จึงไม่ได้กำหนดค่าเริ่มต้น แต่มีการเริ่มต้นʻa`


136

ตามคำตอบที่ยอมรับ (และเท่านั้น) สำหรับคำถาม Stack Overflowนี้

การกำหนดตัวสร้างด้วย

MyTest() = default;

จะทำให้วัตถุเริ่มต้นเป็นศูนย์แทน

แล้วทำไมต่อไปนี้

#include <iostream>

struct foo {
    foo() = default;
    int a;
};

struct bar {
    bar();
    int b;
};

bar::bar() = default;

int main() {
    foo a{};
    bar b{};
    std::cout << a.a << ' ' << b.b;
}

สร้างผลลัพธ์นี้:

0 32766

ตัวสร้างทั้งสองที่กำหนดเป็นค่าเริ่มต้น? ขวา? และสำหรับชนิด POD ค่าเริ่มต้นเริ่มต้นคือศูนย์เริ่มต้น

และเป็นไปตามคำตอบที่ได้รับการยอมรับสำหรับคำถามนี้ ,

  1. หากสมาชิก POD ไม่ได้ถูกเตรียมใช้งานในตัวสร้างหรือผ่านการเริ่มต้นในคลาส C ++ 11 จะถูกกำหนดค่าเริ่มต้นโดยดีฟอลต์

  2. คำตอบเหมือนกันโดยไม่คำนึงถึงกองซ้อนหรือฮีป

  3. ใน C ++ 98 (และไม่ใช่ในภายหลัง), int () ใหม่ถูกระบุว่าดำเนินการเริ่มต้นเป็นศูนย์

แม้จะพยายามตัดหัวของฉัน (แม้ว่าจะเล็ก ) รอบ ๆ ตัวสร้างเริ่มต้นและการเริ่มต้นเริ่มต้นแต่ฉันก็ไม่สามารถหาคำอธิบายได้


3
ที่น่าสนใจฉันยังได้รับคำเตือนสำหรับ b: main.cpp: 18: 34: Warning: 'b.bar::b' ถูกใช้โดยไม่ได้กำหนดค่าเริ่มต้นในฟังก์ชันนี้ [-Wuninitialized] coliru.stacked-crooked.com/a/d1b08a4d6fb4ca7e
tkausl

8
barตัวสร้างของผู้ใช้มีให้ในขณะที่คอนfooสตรัคเตอร์เป็นค่าเริ่มต้น
จรด 42

2
@PeteBecker ฉันเข้าใจแล้ว ฉันจะเขย่า RAM ของฉันเล็กน้อยได้อย่างไรถ้าไม่มีศูนย์อยู่ที่นั่นตอนนี้ควรเป็นอย่างอื่น ;) ps ฉันรันโปรแกรมมาแล้วหลายสิบครั้ง มันไม่ใช่โปรแกรมใหญ่ คุณสามารถเรียกใช้และทดสอบบนระบบของคุณ aเป็นศูนย์ bไม่ใช่. ดูเหมือนว่าaจะเริ่มต้นแล้ว
Duck Dodgers

2
@JoeyMallone เกี่ยวกับ "วิธีที่ผู้ใช้ระบุ": ไม่มีการรับประกันว่าคำจำกัดความของbar::bar()จะปรากฏในmain()- อาจถูกกำหนดในหน่วยการรวบรวมแยกต่างหากและทำสิ่งที่ไม่สำคัญมากในขณะmain()ที่มองเห็นเฉพาะการประกาศเท่านั้น ฉันคิดว่าคุณจะเห็นด้วยว่าพฤติกรรมนี้ไม่ควรเปลี่ยนแปลงขึ้นอยู่กับว่าคุณวางbar::bar()นิยามไว้ในหน่วยคอมไพล์แยกต่างหากหรือไม่ (แม้ว่าสถานการณ์ทั้งหมดจะไม่เกิดขึ้นเองก็ตาม)
Max Langhof

2
@balki หรือint a = 0;คุณต้องการที่จะชัดเจนจริงๆ
NathanOliver

คำตอบ:


110

ประเด็นนี้ค่อนข้างละเอียดอ่อน คุณจะคิดว่า

bar::bar() = default;

จะให้ตัวสร้างเริ่มต้นที่สร้างคอมไพลเลอร์แก่คุณและทำ แต่ตอนนี้ถือว่าเป็นผู้ใช้ที่ให้มา [dcl.fct.def.default] / 5สถานะ:

ฟังก์ชันที่เป็นค่าเริ่มต้นอย่างชัดเจนและฟังก์ชันที่ประกาศโดยนัยเรียกรวมกันว่าฟังก์ชันเริ่มต้นและการนำไปใช้งานจะต้องให้คำจำกัดความโดยนัยสำหรับฟังก์ชันเหล่านี้ ([class.ctor] [class.dtor], [class.copy.ctor], [class.copy.assign ]) ซึ่งอาจหมายถึงการกำหนดเป็นลบ ฟังก์ชันนี้มีให้โดยผู้ใช้หากมีการประกาศโดยผู้ใช้และไม่ได้ตั้งค่าเริ่มต้นหรือลบออกอย่างชัดเจนในการประกาศครั้งแรกฟังก์ชันที่เป็นค่าเริ่มต้นอย่างชัดเจนที่ผู้ใช้ระบุ (กล่าวคือเริ่มต้นอย่างชัดเจนหลังจากการประกาศครั้งแรก) ถูกกำหนดไว้ ณ จุดที่เป็นค่าเริ่มต้นอย่างชัดเจน หากฟังก์ชันดังกล่าวถูกกำหนดโดยปริยายว่าถูกลบแสดงว่าโปรแกรมนั้นมีรูปแบบที่ไม่ถูกต้อง [หมายเหตุ: การประกาศฟังก์ชันเป็นค่าเริ่มต้นหลังจากการประกาศครั้งแรกสามารถให้การดำเนินการที่มีประสิทธิภาพและคำจำกัดความที่กระชับในขณะที่เปิดใช้งานส่วนต่อประสานไบนารีที่เสถียรให้กับฐานรหัสที่กำลังพัฒนา - หมายเหตุ]

เน้นของฉัน

ดังนั้นเราจะเห็นว่าเนื่องจากคุณไม่ได้ตั้งค่าเริ่มต้นbar()เมื่อคุณประกาศครั้งแรกตอนนี้ถือว่าเป็นผู้ใช้ที่ให้ไว้ ด้วยเหตุนั้น[dcl.init] /8.2

ถ้า T เป็นประเภทคลาส (อาจมีคุณสมบัติเป็น cv) โดยไม่มีคอนสตรัคเตอร์ดีฟอลต์ที่ผู้ใช้ระบุหรือถูกลบอ็อบเจ็กต์จะถูกกำหนดค่าเริ่มต้นเป็นศูนย์และจะมีการตรวจสอบข้อ จำกัด ทางความหมายสำหรับการเริ่มต้นดีฟอลต์และหาก T มีคอนสตรัคเตอร์เริ่มต้นที่ไม่สำคัญ วัตถุถูกกำหนดค่าเริ่มต้น

ไม่ใช้อีกต่อไปและเราไม่ได้กำหนดค่าเริ่มต้นbแต่เป็นการเริ่มต้นค่าเริ่มต้นแทน[dcl.init] /8.1

ถ้า T เป็นประเภทคลาส (อาจมีคุณสมบัติ cv) ([คลาส]) ที่ไม่มีตัวสร้างดีฟอลต์ ([class.default.ctor]) หรือตัวสร้างเริ่มต้นที่ผู้ใช้ระบุหรือถูกลบออกจากนั้นอ็อบเจ็กต์จะถูกกำหนดค่าเริ่มต้น ;


52
ฉันหมายถึง(*_*).... ถ้าจะใช้โครงสร้างพื้นฐานของภาษาฉันต้องอ่านแบบร่างภาษาอย่างละเอียดแล้วฮาเลลูยา! แต่มันอาจจะเป็นอย่างที่คุณพูด
Duck Dodgers

12
@balki ใช่การทำbar::bar() = defaultนอกเส้นก็เหมือนกับการทำbar::bar(){}อินไลน์
NathanOliver

15
@JoeyMallone ใช่ C ++ ค่อนข้างซับซ้อน ฉันไม่แน่ใจว่าเหตุผลนี้คืออะไร
NathanOliver

3
หากมีการประกาศก่อนหน้านี้คำจำกัดความที่ตามมาด้วยคีย์เวิร์ดเริ่มต้นจะไม่เริ่มต้นสมาชิกเป็นศูนย์ ขวา? นี่คือความถูกต้อง มันคือสิ่งที่เกิดขึ้นที่นี่
NathanOliver

6
เหตุผลอยู่ในใบเสนอราคาของคุณ: จุดเริ่มต้นนอกบรรทัดคือ "ให้การดำเนินการที่มีประสิทธิภาพและคำจำกัดความที่กระชับในขณะที่เปิดใช้งานส่วนต่อประสานไบนารีที่เสถียรไปยังฐานรหัสที่กำลังพัฒนา" กล่าวอีกนัยหนึ่งคือช่วยให้คุณสามารถเปลี่ยนไป เนื้อหาที่ผู้ใช้เขียนขึ้นในภายหลังหากจำเป็นโดยไม่ทำลาย ABI โปรดทราบว่าคำจำกัดความนอกบรรทัดไม่ได้เป็นแบบอินไลน์โดยปริยายดังนั้นจึงสามารถปรากฏใน TU เดียวตามค่าเริ่มต้น ม ธ . อื่นที่เห็นนิยามคลาสเพียงอย่างเดียวไม่มีทางรู้ได้ว่าถูกกำหนดไว้อย่างชัดเจนว่าเป็นค่าปริยายหรือไม่
TC

25

ความแตกต่างในการทำงานมาจากข้อเท็จจริงที่ว่าตาม[dcl.fct.def.default]/5, bar::barเป็นผู้ใช้ให้ที่foo::fooไม่ได้เป็นที่ 1 เป็นผลให้foo::fooจะมีมูลค่าเริ่มต้นสมาชิก (หมายถึง: ศูนย์การเริ่มต้น foo::a ) แต่bar::barจะยังคงเตรียม2


1) [dcl.fct.def.default]/5

ฟังก์ชันนี้มีให้โดยผู้ใช้หากมีการประกาศโดยผู้ใช้และไม่ได้ตั้งค่าเริ่มต้นหรือลบออกอย่างชัดเจนในการประกาศครั้งแรก

2)

จาก[dcl.init # 6] :

ในการกำหนดค่าเริ่มต้นวัตถุประเภท T หมายถึง:

  • ถ้า T เป็นประเภทคลาส (อาจมีคุณสมบัติเป็น cv) ที่ไม่มีตัวสร้างเริ่มต้น ([class.ctor]) หรือตัวสร้างดีฟอลต์ที่ผู้ใช้ระบุหรือลบออกจากนั้นอ็อบเจ็กต์จะถูกกำหนดค่าเริ่มต้น

  • ถ้า T เป็นประเภทคลาส (อาจมีคุณสมบัติเป็น cv) โดยไม่มีคอนสตรัคเตอร์ดีฟอลต์ที่ผู้ใช้ระบุหรือถูกลบอ็อบเจ็กต์จะถูกกำหนดค่าเริ่มต้นเป็นศูนย์และจะมีการตรวจสอบข้อ จำกัด เชิงความหมายสำหรับการเริ่มต้นเริ่มต้นและหาก T มีตัวสร้างเริ่มต้นที่ไม่สำคัญ วัตถุถูกกำหนดค่าเริ่มต้นเป็นค่าเริ่มต้น

  • ...

จาก[dcl.init.list] :

การเริ่มต้นรายการของวัตถุหรือการอ้างอิงประเภท T ถูกกำหนดดังนี้:

  • ...

  • มิฉะนั้นหากรายการ initializer ไม่มีองค์ประกอบและ T เป็นประเภทคลาสที่มีตัวสร้างเริ่มต้นอ็อบเจ็กต์จะถูกกำหนดค่าเริ่มต้น

จากคำตอบของ Vittorio Romeo


10

จากcppreference :

การเตรียมใช้งานแบบรวมเริ่มต้นการรวม เป็นรูปแบบของรายการเริ่มต้น

การรวมเป็นหนึ่งในประเภทต่อไปนี้:

[snip]

  • ประเภทคลาส [snip] ที่มี

    • [snip] (มีรูปแบบสำหรับเวอร์ชันมาตรฐานที่แตกต่างกัน)

    • ไม่มีตัวสร้างที่ผู้ใช้ระบุสืบทอดหรือชัดเจน (อนุญาตให้ใช้ตัวสร้างที่ผิดนัดหรือถูกลบอย่างชัดเจน)

    • [snip] (มีกฎเพิ่มเติมซึ่งใช้กับทั้งสองคลาส)

ให้คำจำกัดความfooนี้เป็นการรวมในขณะที่barไม่ใช่ (มีตัวสร้างที่ผู้ใช้กำหนดและไม่ใช่ค่าเริ่มต้น)

ดังนั้นสำหรับfoo, T object {arg1, arg2, ...};ไวยากรณ์สำหรับการรวม initialisation

ผลกระทบของการเริ่มต้นรวมคือ:

  • [snip] (รายละเอียดบางอย่างที่ไม่เกี่ยวข้องกับกรณีนี้)

  • ถ้าจำนวนข้อ initializer น้อยกว่าจำนวนสมาชิกหรือการเริ่มต้นรายการจะสมบูรณ์ว่างที่เหลือสมาชิกที่มีมูลค่าเริ่มต้น

ดังนั้นa.aค่าเริ่มต้นซึ่งintหมายถึงการเริ่มต้นเป็นศูนย์

สำหรับbar, T object {};บนมืออื่น ๆ คือค่า initialisation (อินสแตนซ์ระดับที่ไม่คุ้มค่า initialisation สมาชิก!) เนื่องจากเป็นประเภทคลาสที่มีตัวสร้างเริ่มต้นจึงเรียกตัวสร้างเริ่มต้น ตัวสร้างดีฟอลต์ที่คุณกำหนดค่าเริ่มต้นเริ่มต้นของสมาชิก (โดยอาศัยอำนาจของการไม่มีตัวเริ่มต้นสมาชิก) ซึ่งในกรณีของint(ที่มีหน่วยเก็บข้อมูลแบบไม่คงที่) จะปล่อยb.bให้มีค่าที่ไม่แน่นอน

และสำหรับ pod-types การเริ่มต้นดีฟอลต์คือ zero-initialization

ไม่นี่เป็นสิ่งที่ผิด


ป.ล. คำเกี่ยวกับการทดสอบและข้อสรุปของคุณ: การเห็นว่าผลลัพธ์เป็นศูนย์ไม่จำเป็นต้องหมายความว่าตัวแปรเริ่มต้นเป็นศูนย์ ศูนย์เป็นตัวเลขที่เป็นไปได้อย่างสมบูรณ์แบบสำหรับมูลค่าขยะ

สำหรับการที่ฉันรันโปรแกรมอาจจะ 5 ~ 6 ครั้งก่อนโพสต์และประมาณ 10 ครั้งตอนนี้ a เป็นศูนย์ b มีการเปลี่ยนแปลงเล็กน้อย

ความจริงที่ว่าค่าเท่ากันหลาย ๆ ครั้งไม่ได้หมายความว่าจะเริ่มต้นด้วย

ฉันลองใช้ชุดด้วย (CMAKE_CXX_STANDARD 14) ผลก็เหมือนเดิม

ความจริงที่ว่าผลลัพธ์เหมือนกันกับตัวเลือกคอมไพเลอร์หลายตัวไม่ได้หมายความว่าตัวแปรนั้นเริ่มต้นแล้ว (แม้ว่าในบางกรณีการเปลี่ยนเวอร์ชันมาตรฐานสามารถเปลี่ยนแปลงได้ว่าจะเริ่มต้นหรือไม่)

ฉันจะเขย่า RAM ของฉันเล็กน้อยได้อย่างไรถ้าไม่มีศูนย์อยู่ที่นั่นตอนนี้ควรเป็นอย่างอื่น

ไม่มีวิธีใดที่รับประกันได้ใน C ++ ที่จะทำให้ค่าที่ไม่ได้กำหนดค่าเริ่มต้นปรากฏไม่ใช่ศูนย์

วิธีเดียวที่จะทราบว่ามีการเริ่มต้นตัวแปรคือการเปรียบเทียบโปรแกรมกับกฎของภาษาและตรวจสอบว่ากฎบอกว่าเริ่มต้นแล้ว ในกรณีนี้a.aคือการเริ่มต้นแน่นอน


"ตัวสร้างเริ่มต้นที่คุณกำหนดค่าเริ่มต้นเริ่มต้นสมาชิก (โดยอาศัยอำนาจของการไม่มีตัวเริ่มต้นสมาชิก) ซึ่งในกรณีของ int จะปล่อยให้มันมีค่าที่ไม่แน่นอน" -> เอ๊ะ! "สำหรับประเภทพ็อดค่าเริ่มต้นเริ่มต้นคือศูนย์เริ่มต้น" หรือฉันผิด?
Duck Dodgers

2
@JoeyMallone การกำหนดค่าเริ่มต้นของประเภท POD เป็นค่าเริ่มต้นไม่ได้
NathanOliver

@NathanOliver จากนั้นฉันก็ยิ่งสับสน แล้วaจะเริ่มต้นได้อย่างไร ฉันคิดว่าaเป็นค่าเริ่มต้นเริ่มต้นและการเริ่มต้นเริ่มต้นสำหรับสมาชิก POD คือการเริ่มต้นเป็นศูนย์ เป็นaแล้วก็โชคดีที่มักจะขึ้นมาเป็นศูนย์ว่ากี่ครั้งที่ผมไม่เรียกใช้โปรแกรมนี้
Duck Dodgers

@JoeyMallone Then how come a is initialized.เพราะมันเป็นค่าเริ่มต้น I was thinking a is default initializedมันไม่ใช่.
eerorika

3
@JoeyMallone อย่าเพิ่งกังวลไป คุณสามารถสร้างหนังสือจากการเริ่มต้นใน C ++ หากคุณมีโอกาส CppCon บน youtube มีวิดีโอบางส่วนเกี่ยวกับการเริ่มต้นด้วยสิ่งที่น่าผิดหวังที่สุด (ดังที่ชี้ให้เห็นว่ามันแย่แค่ไหน) เป็นyoutube.com/watch?v=7DTlWPgX6zs
NathanOliver

0

ฉันลองเรียกใช้ตัวอย่างข้อมูลที่คุณระบุtest.cppผ่าน gcc & clang และระดับการเพิ่มประสิทธิภาพหลายระดับ:

steve@steve-pc /tmp> g++ -o test.gcc.O0 test.cpp
                                                                              [ 0s828 | Jan 27 01:16PM ]
steve@steve-pc /tmp> g++ -o test.gcc.O2 -O2 test.cpp
                                                                              [ 0s901 | Jan 27 01:16PM ]
steve@steve-pc /tmp> g++ -o test.gcc.Os -Os test.cpp
                                                                              [ 0s875 | Jan 27 01:16PM ]
steve@steve-pc /tmp> ./test.gcc.O0
0 32764                                                                       [ 0s004 | Jan 27 01:16PM ]
steve@steve-pc /tmp> ./test.gcc.O2
0 0                                                                           [ 0s004 | Jan 27 01:16PM ]
steve@steve-pc /tmp> ./test.gcc.Os
0 0                                                                           [ 0s003 | Jan 27 01:16PM ]
steve@steve-pc /tmp> clang++ -o test.clang.O0 test.cpp
                                                                              [ 1s089 | Jan 27 01:17PM ]
steve@steve-pc /tmp> clang++ -o test.clang.Os -Os test.cpp
                                                                              [ 1s058 | Jan 27 01:17PM ]
steve@steve-pc /tmp> clang++ -o test.clang.O2 -O2 test.cpp
                                                                              [ 1s109 | Jan 27 01:17PM ]
steve@steve-pc /tmp> ./test.clang.O0
0 274247888                                                                   [ 0s004 | Jan 27 01:17PM ]
steve@steve-pc /tmp> ./test.clang.Os
0 0                                                                           [ 0s004 | Jan 27 01:17PM ]
steve@steve-pc /tmp> ./test.clang.O2
0 0                                                                           [ 0s004 | Jan 27 01:17PM ]
steve@steve-pc /tmp> ./test.clang.O0
0 2127532240                                                                  [ 0s002 | Jan 27 01:18PM ]
steve@steve-pc /tmp> ./test.clang.O0
0 344211664                                                                   [ 0s004 | Jan 27 01:18PM ]
steve@steve-pc /tmp> ./test.clang.O0
0 1694408912                                                                  [ 0s004 | Jan 27 01:18PM ]

นั่นคือจุดที่น่าสนใจมันแสดงให้เห็นอย่างชัดเจนว่า clang O0 build กำลังอ่านตัวเลขสุ่มซึ่งน่าจะเป็น stack space

ฉันรีบเปิด IDA เพื่อดูว่าเกิดอะไรขึ้น:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rax
  __int64 v4; // rax
  int result; // eax
  unsigned int v6; // [rsp+8h] [rbp-18h]
  unsigned int v7; // [rsp+10h] [rbp-10h]
  unsigned __int64 v8; // [rsp+18h] [rbp-8h]

  v8 = __readfsqword(0x28u); // alloca of 0x28
  v7 = 0; // this is foo a{}
  bar::bar((bar *)&v6); // this is bar b{}
  v3 = std::ostream::operator<<(&std::cout, v7); // this is clearly 0
  v4 = std::operator<<<std::char_traits<char>>(v3, 32LL); // 32 = 0x20 = ' '
  result = std::ostream::operator<<(v4, v6); // joined as cout << a.a << ' ' << b.b, so this is reading random values!!
  if ( __readfsqword(0x28u) == v8 ) // stack align check
    result = 0;
  return result;
}

ตอนนี้bar::bar(bar *this)ทำอะไร?

void __fastcall bar::bar(bar *this)
{
  ;
}

อืมไม่มีอะไร เราต้องหันไปใช้การประกอบ:

.text:00000000000011D0                               ; __int64 __fastcall bar::bar(bar *__hidden this)
.text:00000000000011D0                                               public _ZN3barC2Ev
.text:00000000000011D0                               _ZN3barC2Ev     proc near               ; CODE XREF: main+20↓p
.text:00000000000011D0
.text:00000000000011D0                               var_8           = qword ptr -8
.text:00000000000011D0
.text:00000000000011D0                               ; __unwind {
.text:00000000000011D0 55                                            push    rbp
.text:00000000000011D1 48 89 E5                                      mov     rbp, rsp
.text:00000000000011D4 48 89 7D F8                                   mov     [rbp+var_8], rdi
.text:00000000000011D8 5D                                            pop     rbp
.text:00000000000011D9 C3                                            retn
.text:00000000000011D9                               ; } // starts at 11D0
.text:00000000000011D9                               _ZN3barC2Ev     endp

this = thisเพื่อใช่มันเป็นเพียงแค่ไม่มีอะไรสิ่งที่สร้างโดยทั่วไปจะเป็น แต่เรารู้ว่าจริงๆแล้วมันกำลังโหลดที่อยู่สแต็กที่ไม่ได้เริ่มต้นแบบสุ่มและพิมพ์

จะเกิดอะไรขึ้นถ้าเราระบุค่าสำหรับโครงสร้างทั้งสองอย่างชัดเจน?

#include <iostream>

struct foo {
    foo() = default;
    int a;
};

struct bar {
    bar();
    int b;
};

bar::bar() = default;

int main() {
    foo a{0};
    bar b{0};
    std::cout << a.a << ' ' << b.b;
}

ตีเสียงดังอุ๊ย:

steve@steve-pc /tmp> clang++ -o test.clang.O0 test.cpp
test.cpp:17:9: error: no matching constructor for initialization of 'bar'
    bar b{0};
        ^~~~
test.cpp:8:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion
      from 'int' to 'const bar' for 1st argument
struct bar {
       ^
test.cpp:8:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion
      from 'int' to 'bar' for 1st argument
struct bar {
       ^
test.cpp:13:6: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
bar::bar() = default;
     ^
1 error generated.
                                                                              [ 0s930 | Jan 27 01:35PM ]

ชะตากรรมที่คล้ายกันกับ g ++ เช่นกัน:

steve@steve-pc /tmp> g++ test.cpp
test.cpp: In function ‘int main()’:
test.cpp:17:12: error: no matching function for call to ‘bar::bar(<brace-enclosed initializer list>)’
     bar b{0};
            ^
test.cpp:8:8: note: candidate: ‘bar::bar()’
 struct bar {
        ^~~
test.cpp:8:8: note:   candidate expects 0 arguments, 1 provided
test.cpp:8:8: note: candidate: ‘constexpr bar::bar(const bar&)’
test.cpp:8:8: note:   no known conversion for argument 1 from ‘int’ to ‘const bar&’
test.cpp:8:8: note: candidate: ‘constexpr bar::bar(bar&&)’
test.cpp:8:8: note:   no known conversion for argument 1 from ‘int’ to ‘bar&&’
                                                                              [ 0s718 | Jan 27 01:35PM ]

นั่นหมายความว่าเป็นการเริ่มต้นโดยตรงอย่างมีประสิทธิภาพbar b(0)ไม่ใช่การเริ่มต้นรวม

อาจเป็นเพราะหากคุณไม่ได้ระบุการใช้งานตัวสร้างที่ชัดเจนสิ่งนี้อาจเป็นสัญลักษณ์ภายนอกตัวอย่างเช่น:

bar::bar() {
  this.b = 1337; // whoa
}

คอมไพเลอร์ไม่ฉลาดพอที่จะอนุมานสิ่งนี้ว่าเป็น no-op / การโทรแบบอินไลน์ในขั้นตอนที่ไม่ได้ปรับให้เหมาะสม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.