เหตุใด C ++ 11 จึงไม่รองรับรายการ initializer ที่กำหนดเป็น C99 [ปิด]


121

พิจารณา:

struct Person
{
    int height;
    int weight;
    int age;
};

int main()
{
    Person p { .age = 18 };
}

รหัสด้านบนถูกต้องตามกฎหมายใน C99 แต่ไม่ถูกกฎหมายใน C ++ 11

สิ่งที่เป็น เหตุผลของคณะกรรมการมาตรฐานในการยกเว้นการสนับสนุนคุณลักษณะที่มีประโยชน์เช่นนี้หรือไม่?


10
เห็นได้ชัดว่าคณะกรรมการออกแบบไม่เหมาะสมที่จะรวมไว้หรือไม่เกิดขึ้นในการประชุม เป็นที่น่าสังเกตว่าตัวเริ่มต้นที่กำหนด C99 ไม่ได้อยู่ในเวอร์ชันข้อกำหนด C ++ ใด ๆ คอนสตรัคเตอร์ดูเหมือนจะเป็นโครงสร้างเริ่มต้นที่ต้องการและด้วยเหตุผลที่ดีพวกเขารับประกันการเริ่มต้นอ็อบเจ็กต์ที่สอดคล้องกันหากคุณเขียนอย่างถูกต้อง
Robert Harvey

19
การใช้เหตุผลของคุณล้าหลังภาษาไม่จำเป็นต้องมีเหตุผลสำหรับการไม่มีคุณลักษณะจำเป็นต้องมีเหตุผลในการมีอย่างใดอย่างหนึ่งและแข็งแกร่งในนั้น C ++ ป่องพอที่จะยืน
Matthieu M.

42
เหตุผลที่ดี (ซึ่งไม่สามารถแก้ไขได้ด้วยคอนสตรัคเตอร์ยกเว้นโดยการเขียน Wrapefying Wrapper) คือไม่ว่าคุณจะใช้ C ++ หรือไม่ก็ตาม API จริงส่วนใหญ่จะเป็น C ไม่ใช่ C ++ และมีเพียงไม่กี่ตัวเท่านั้นที่ทำให้คุณมีโครงสร้างที่คุณต้องการตั้งค่า หนึ่งหรือสองฟิลด์ - และไม่จำเป็นต้องเป็นฟิลด์แรก - แต่จำเป็นต้องมีค่าเริ่มต้นที่เหลือเป็นศูนย์ Win32 API OVERLAPPEDเป็นตัวอย่าง ความสามารถในการเขียน={.Offset=12345};จะทำให้โค้ดชัดเจนขึ้นมาก (และอาจเกิดข้อผิดพลาดน้อยกว่า) ซ็อกเก็ต BSD เป็นตัวอย่างที่คล้ายกัน
Damon

14
รหัสในmainไม่ถูกต้องตามกฎหมาย C99 น่าอ่าน struct Person p = { .age = 18 };
chqrlie

14
FYI C ++ 20 จะรองรับตัวเริ่มต้นที่กำหนด
Andrew Tomazos

คำตอบ:


34

C ++ มีตัวสร้าง หากเหมาะสมที่จะเริ่มต้นสมาชิกเพียงคนเดียวก็สามารถแสดงในโปรแกรมได้โดยใช้ตัวสร้างที่เหมาะสม นี่คือประเภทของการส่งเสริม C ++ ที่เป็นนามธรรม

ในทางกลับกันคุณลักษณะ initializers ที่กำหนดนั้นเป็นข้อมูลเพิ่มเติมเกี่ยวกับการเปิดเผยและทำให้สมาชิกเข้าถึงได้ง่ายโดยตรงในรหัสไคลเอนต์ สิ่งนี้นำไปสู่สิ่งต่างๆเช่นการมีคนอายุ 18 ปี (ปี?) แต่มีส่วนสูงและน้ำหนักเป็นศูนย์


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

C ++ สนใจที่จะวางความยืดหยุ่นไว้ที่ด้านข้างของนักออกแบบประเภทหนึ่งแทนดังนั้นนักออกแบบจึงสามารถใช้ประเภทได้อย่างถูกต้องและยากที่จะใช้อย่างไม่ถูกต้อง การกำหนดให้นักออกแบบสามารถควบคุมวิธีการเตรียมใช้งานประเภทได้เป็นส่วนหนึ่งของสิ่งนี้: นักออกแบบกำหนดตัวสร้างตัวเริ่มต้นในคลาส ฯลฯ


12
โปรดแสดงลิงค์อ้างอิงสำหรับสิ่งที่คุณพูดคือสาเหตุที่ C ++ ไม่ได้กำหนดตัวเริ่มต้น ฉันจำไม่ได้ว่าเคยเห็นข้อเสนอของมัน
Johannes Schaub - litb

20
ไม่ใช่เหตุผลPersonที่ผู้สร้างต้องการให้ผู้สร้างมีความยืดหยุ่นมากที่สุดในการตั้งค่าและเริ่มต้นสมาชิกหรือไม่ ผู้ใช้สามารถเขียนได้แล้วPerson p = { 0, 0, 18 };(และด้วยเหตุผลที่ดี)
Johannes Schaub - litb

7
สิ่งที่คล้ายกันเมื่อเร็ว ๆ นี้ได้รับการยอมรับใน C ++ 14 สเปคโดยopen-std.org/jtc1/sc22/wg21/docs/papers/2013/n3605.html
Johannes Schaub - litb

4
@ JohannesSchaub-litb ฉันไม่ได้พูดถึงสาเหตุเชิงกลและเชิงใกล้เคียงอย่างแท้จริง (กล่าวคือยังไม่ได้เสนอต่อคณะกรรมการ) ฉันกำลังอธิบายถึงสิ่งที่ฉันเชื่อว่าเป็นปัจจัยที่มีอำนาจเหนือกว่า - Personมีการออกแบบ C มากดังนั้นคุณสมบัติ C จึงสมเหตุสมผล อย่างไรก็ตาม C ++ อาจช่วยให้สามารถออกแบบได้ดีขึ้นซึ่งไม่จำเป็นต้องใช้ตัวเริ่มต้นที่กำหนด - ในมุมมองของฉันการลบข้อ จำกัด ในตัวเริ่มต้นในคลาสสำหรับการรวมนั้นสอดคล้องกับ ethos ของ C ++ มากกว่าตัวเริ่มต้นที่กำหนด
bames53

4
การแทนที่ C ++ สำหรับสิ่งนี้อาจมีชื่อว่าอาร์กิวเมนต์ของฟังก์ชัน แต่ ณ ตอนนี้อาร์กิวเมนต์ชื่อยังไม่มีอย่างเป็นทางการ ดูN4172 อาร์กิวเมนต์ที่มีชื่อสำหรับข้อเสนอนี้ มันจะทำให้รหัสเกิดข้อผิดพลาดน้อยลงและอ่านง่ายขึ้น
David Baird

89

วันที่ 15 กรกฎาคม2560 P0329R4ได้รับการยอมรับในมาตรฐาน: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdf
สิ่งนี้นำมาซึ่งการสนับสนุนที่ จำกัด สำหรับInitializers ที่กำหนดไว้ ข้อ จำกัด นี้อธิบายไว้ดังต่อไปนี้โดย C.1.7 [diff.decl] .4 กำหนด:

struct A { int x, y; };
struct B { struct A a; };

การเริ่มต้นที่กำหนดไว้ต่อไปนี้ซึ่งใช้ได้ใน C ถูก จำกัด ใน C ++:

  • struct A a = { .y = 1, .x = 2 } ไม่ถูกต้องใน C ++ เนื่องจากตัวกำหนดต้องปรากฏในลำดับการประกาศของสมาชิกข้อมูล
  • int arr[3] = { [1] = 5 } ไม่ถูกต้องใน C ++ เนื่องจากไม่รองรับการกำหนดค่าเริ่มต้นของอาร์เรย์
  • struct B b = {.a.x = 0} ไม่ถูกต้องใน C ++ เนื่องจากตัวกำหนดไม่สามารถซ้อนกันได้
  • struct A c = {.x = 1, 2} ไม่ถูกต้องใน C ++ เนื่องจากสมาชิกข้อมูลทั้งหมดหรือทั้งหมดต้องได้รับการเตรียมใช้งานโดยผู้ออกแบบ

สำหรับ และก่อนหน้านี้ Boost ได้รองรับ Designated Intializersและมีข้อเสนอมากมายเพื่อเพิ่มการสนับสนุนให้กับมาตรฐานตัวอย่างเช่น: n4172และข้อเสนอ Daryle วอล์คเกอร์ที่จะเพิ่มการกำหนดที่จะ Initializers ข้อเสนออ้างถึงการนำไปใช้ตัวเริ่มต้นที่กำหนดไว้ใน Visual C ++, gcc และ Clang โดยอ้างว่า:

เราเชื่อว่าการเปลี่ยนแปลงจะค่อนข้างตรงไปตรงมาในการนำไปใช้

แต่คณะกรรมการมาตรฐานปฏิเสธข้อเสนอดังกล่าวซ้ำแล้วซ้ำอีกโดยระบุว่า:

EWG พบปัญหาต่างๆเกี่ยวกับแนวทางที่เสนอและไม่คิดว่าจะเป็นไปได้ที่จะลองแก้ปัญหาเนื่องจากมีการพยายามหลายครั้งและทุกครั้งที่ล้มเหลว

ความคิดเห็นของ Ben Voigtช่วยให้ฉันเห็นปัญหาที่ผ่านไม่ได้ด้วยวิธีนี้ ได้รับ:

struct X {
    int c;
    char a;
    float b;
};

ฟังก์ชันเหล่านี้จะถูกเรียกในลำดับใด : struct X foo = {.a = (char)f(), .b = g(), .c = h()}? น่าแปลกที่ใน:

ลำดับของการประเมินนิพจน์ย่อยในตัวเริ่มต้นใด ๆ จะเรียงตามลำดับอย่างไม่แน่นอน[ 1 ]

(Visual C ++, gccและ Clang ดูเหมือนจะเห็นด้วยกับพฤติกรรมเนื่องจากพวกเขาทั้งหมดจะทำการโทรตามลำดับนี้ :)

  1. h()
  2. f()
  3. g()

แต่ลักษณะที่ไม่แน่นอนของมาตรฐานหมายความว่าหากฟังก์ชันเหล่านี้มีปฏิสัมพันธ์ใด ๆ สถานะของโปรแกรมที่เป็นผลลัพธ์ก็จะไม่แน่นอนและคอมไพเลอร์จะไม่เตือนคุณ : มีวิธีรับคำเตือนเกี่ยวกับการทำงานที่ไม่ถูกต้องของตัวเริ่มต้นที่กำหนดไว้หรือไม่?

ไม่ได้มีความต้องการที่เข้มงวด initializer รายการ 11.6.4 [dcl.init.list] 4:

ภายใน initializer-list ของ braced-init-list, initializer-clauses รวมถึงผลลัพธ์จากการขยายแพ็ก (17.5.3) จะได้รับการประเมินตามลำดับที่ปรากฏ นั่นคือทุกการคำนวณค่าและด้าน e ff ect ที่เกี่ยวข้องกับ initializer-clause ที่กำหนดจะถูกจัดลำดับก่อนการคำนวณค่าและผลข้างเคียงที่เกี่ยวข้องกับ initializer-clause ใด ๆ ที่ตามมาในรายการที่คั่นด้วยเครื่องหมายจุลภาคของ initializer-list

ดังนั้น การสนับสนุนจะต้องดำเนินการตามลำดับ:

  1. f()
  2. g()
  3. h()

ทำลายความเข้ากันได้กับรุ่นก่อนหน้า การใช้งาน
ตามที่กล่าวไว้ข้างต้นปัญหานี้ได้รับการหลีกเลี่ยงโดยข้อ จำกัด ของ Designated Initializers ที่ยอมรับใน. มีลักษณะการทำงานที่เป็นมาตรฐานซึ่งรับประกันลำดับการดำเนินการของตัวเริ่มต้นที่กำหนด


3
แน่นอนว่าในรหัสนี้: struct X { int c; char a; float b; }; X x = { .a = f(), .b = g(), .c = h() };การเรียกร้องให้h()มีการดำเนินการก่อนอย่างใดอย่างหนึ่งหรือf() g()หากคำจำกัดความของคำว่าstruct Xไม่ใกล้เคียงสิ่งนี้จะน่าแปลกใจมาก โปรดจำไว้ว่านิพจน์ initializer ไม่จำเป็นต้องปราศจากผลข้างเคียง
Ben Voigt

2
แน่นอนว่านี่ไม่ใช่เรื่องใหม่การเริ่มต้นสมาชิก ctor มีปัญหานี้อยู่แล้ว แต่มันอยู่ในคำจำกัดความของสมาชิกคลาสดังนั้นการมีเพศสัมพันธ์ที่แน่นจึงไม่แปลกใจ และตัวเริ่มต้นที่กำหนดไม่สามารถอ้างอิงสมาชิกคนอื่น ๆ ในแบบที่สมาชิก ctor-initializers ทำได้
Ben Voigt

2
@MattMcNabb: ไม่มันไม่สุดโต่งกว่านี้ แต่คาดว่านักพัฒนาที่ใช้ตัวสร้างคลาสจะรู้คำสั่งการประกาศสมาชิก ในขณะที่ผู้บริโภคในชั้นเรียนอาจเป็นโปรแกรมเมอร์คนอื่นโดยสิ้นเชิง เนื่องจากประเด็นทั้งหมดคือการอนุญาตให้เริ่มต้นโดยไม่ต้องมองหาลำดับของสมาชิกสิ่งนี้จึงดูเหมือนข้อบกพร่องร้ายแรงในข้อเสนอ เนื่องจากตัวเริ่มต้นที่กำหนดไม่สามารถอ้างอิงวัตถุที่กำลังสร้างได้การแสดงผลครั้งแรกคือนิพจน์การเริ่มต้นสามารถประเมินได้ก่อนตามลำดับการกำหนดจากนั้นจึงเริ่มต้นสมาชิกตามลำดับการประกาศ แต่ ...
Ben Voigt

2
@JonathanMee: คำถามอื่นตอบว่า ... ตัวเริ่มต้นรวม C99 ไม่ได้เรียงลำดับดังนั้นจึงไม่มีความคาดหวังว่าจะมีการสั่ง initializers ที่กำหนดไว้ C ++ braced-init-รายการเรียงตามลำดับและข้อเสนอสำหรับตัวเริ่มต้นที่กำหนดจะใช้คำสั่งที่น่าแปลกใจ (คุณไม่สามารถสอดคล้องทั้งกับลำดับคำศัพท์ที่ใช้สำหรับรายการ braced-init และลำดับสมาชิกทั้งหมดที่ใช้สำหรับ ctor-initializer
Ben Voigt

3
โจนาธาน: "การสนับสนุน c ++ จำเป็นต้องดำเนินการตามลำดับ [... ] ทำลายความเข้ากันได้กับการใช้งาน c99 ก่อนหน้านี้" ฉันไม่ได้รับอันนี้ขออภัย 1. หากคำสั่งไม่แน่นอนใน C99 เห็นได้ชัดว่าคำสั่งซื้อจริงใด ๆควรจะใช้ได้รวมถึงตัวเลือก C ++ โดยพลการ b) ไม่สนับสนุน des. initializers เลยทำลายความเข้ากันได้ของ C99 มากยิ่งขึ้น ...
Sz.

34

แฮกเกอร์นิดหน่อยดังนั้นแค่แบ่งปันเพื่อความสนุกสนาน

#define with(T, ...)\
    ([&]{ T ${}; __VA_ARGS__; return $; }())

และใช้มันเช่น:

MyFunction(with(Params,
    $.Name = "Foo Bar",
    $.Age  = 18
));

ซึ่งขยายเป็น:

MyFunction(([&] {
 Params ${};
 $.Name = "Foo Bar", $.Age = 18;
 return $;
}()));

เรียบร้อยสร้างแลมบ์ดาที่มีตัวแปรชื่อ$ประเภทTและคุณกำหนดสมาชิกโดยตรงก่อนส่งคืน ดี ฉันสงสัยว่ามีปัญหาด้านประสิทธิภาพหรือไม่
TankorSmash

1
ในโครงสร้างที่ได้รับการปรับให้เหมาะสมคุณจะไม่เห็นร่องรอยของแลมด้าหรือการร้องขอ เป็นแบบอินไลน์ทั้งหมด
keebus

1
ฉันชอบคำตอบนี้มาก
Seph Reed

6
ว้าว ไม่รู้ด้วยซ้ำว่า $ เป็นชื่อที่ถูกต้อง
Chris Watts

ได้รับการสนับสนุนโดยคอมไพเลอร์ C ดั้งเดิมและการสนับสนุนยังคงอยู่เพื่อความเข้ากันได้แบบย้อนหลัง
keebus

22

ขณะนี้เครื่องมือเริ่มต้นที่กำหนดไว้จะรวมอยู่ในเนื้องาน C ++ 20: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdfดังนั้นในที่สุดเราก็อาจจะเห็นมัน!


3
แต่โปรดทราบว่าพวกเขาถูก จำกัด : ใน C ++ การสนับสนุนการกำหนดค่าเริ่มต้นที่กำหนดจะถูก จำกัด เมื่อเทียบกับฟังก์ชันการทำงานที่สอดคล้องกันใน C ใน C ++ ตัวกำหนดสำหรับสมาชิกข้อมูลที่ไม่คงที่ต้องระบุในลำดับการประกาศตัวกำหนดองค์ประกอบอาร์เรย์และตัวกำหนดที่ซ้อนกันจะไม่ ตัวเริ่มต้นที่รองรับและกำหนดและไม่ได้กำหนดไม่สามารถผสมในรายการตัวเริ่มต้นเดียวกันได้ ซึ่งหมายความว่าโดยเฉพาะอย่างยิ่งคุณจะยังคงไม่สามารถที่จะได้อย่างง่ายดายทำให้ตารางการค้นหา enum-คีย์
Ruslan

@Ruslan: ฉันสงสัยว่าทำไม C ++ ถึง จำกัด มันมากขนาดนี้? ฉันเข้าใจว่าอาจมีความสับสนว่าลำดับที่ค่าของรายการได้รับการประเมินและ / หรือเขียนลงในโครงสร้างตรงกับลำดับที่ระบุรายการในรายการ initalization หรือลำดับที่สมาชิกปรากฏในโครงสร้าง แต่ วิธีแก้ปัญหานั้นก็คือการบอกว่านิพจน์การเริ่มต้นจะดำเนินการตามลำดับโดยพลการและอายุการใช้งานของวัตถุจะไม่เริ่มต้นจนกว่าการเตรียมใช้งานจะเสร็จสมบูรณ์ (ตัว&ดำเนินการจะส่งคืนที่อยู่ที่วัตถุจะมีในช่วงอายุการใช้งาน)
supercat

5

คุณสมบัติหลักของ C99 สองประการที่ C ++ 11 ขาดกล่าวถึง“ ตัวเริ่มต้นที่กำหนดและ C ++”

ฉันคิดว่า 'ตัวเริ่มต้นที่กำหนด' เกี่ยวข้องกับการเพิ่มประสิทธิภาพที่เป็นไปได้ ที่นี่ฉันใช้“ gcc / g ++” 5.1 เป็นตัวอย่าง

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>    
struct point {
    int x;
    int y;
};
const struct point a_point = {.x = 0, .y = 0};
int foo() {
    if(a_point.x == 0){
        printf("x == 0");
        return 0;
    }else{
        printf("x == 1");
        return 1;
    }
}
int main(int argc, char *argv[])
{
    return foo();
}

เรารู้ว่าในเวลารวบรวมa_point.xเป็นศูนย์เพื่อให้เราสามารถคาดหวังว่ามีการเพิ่มประสิทธิภาพเป็นหนึ่งfooprintf

$ gcc -O3 a.c
$ gdb a.out
(gdb) disassemble foo
Dump of assembler code for function foo:
   0x00000000004004f0 <+0>: sub    $0x8,%rsp
   0x00000000004004f4 <+4>: mov    $0x4005bc,%edi
   0x00000000004004f9 <+9>: xor    %eax,%eax
   0x00000000004004fb <+11>:    callq  0x4003a0 <printf@plt>
   0x0000000000400500 <+16>:    xor    %eax,%eax
   0x0000000000400502 <+18>:    add    $0x8,%rsp
   0x0000000000400506 <+22>:    retq   
End of assembler dump.
(gdb) x /s 0x4005bc
0x4005bc:   "x == 0"

fooได้รับการปรับให้เหมาะกับการพิมพ์x == 0เท่านั้น

สำหรับเวอร์ชัน C ++

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
struct point {
    point(int _x,int _y):x(_x),y(_y){}
    int x;
    int y;
};
const struct point a_point(0,0);
int foo() {
    if(a_point.x == 0){
        printf("x == 0");
        return 0;
    }else{
        printf("x == 1");
        return 1;
    }
}
int main(int argc, char *argv[])
{
    return foo();
}

และนี่คือผลลัพธ์ของโค้ดแอสเซมบลีที่ปรับให้เหมาะสม

g++ -O3 a.cc
$ gdb a.out
(gdb) disassemble foo
Dump of assembler code for function _Z3foov:
0x00000000004005c0 <+0>:    push   %rbx
0x00000000004005c1 <+1>:    mov    0x200489(%rip),%ebx        # 0x600a50 <_ZL7a_point>
0x00000000004005c7 <+7>:    test   %ebx,%ebx
0x00000000004005c9 <+9>:    je     0x4005e0 <_Z3foov+32>
0x00000000004005cb <+11>:   mov    $0x1,%ebx
0x00000000004005d0 <+16>:   mov    $0x4006a3,%edi
0x00000000004005d5 <+21>:   xor    %eax,%eax
0x00000000004005d7 <+23>:   callq  0x400460 <printf@plt>
0x00000000004005dc <+28>:   mov    %ebx,%eax
0x00000000004005de <+30>:   pop    %rbx
0x00000000004005df <+31>:   retq   
0x00000000004005e0 <+32>:   mov    $0x40069c,%edi
0x00000000004005e5 <+37>:   xor    %eax,%eax
0x00000000004005e7 <+39>:   callq  0x400460 <printf@plt>
0x00000000004005ec <+44>:   mov    %ebx,%eax
0x00000000004005ee <+46>:   pop    %rbx
0x00000000004005ef <+47>:   retq   

เราจะเห็นได้ว่านั่นa_pointไม่ใช่ค่าคงที่ของเวลาคอมไพล์


8
constexpr point(int _x,int _y):x(_x),y(_y){}ตอนนี้โปรดลอง เครื่องมือเพิ่มประสิทธิภาพของ clang ++ ดูเหมือนจะกำจัดการเปรียบเทียบในโค้ดของคุณด้วย นี่เป็นเพียงปัญหา QoI
dyp

ฉันยังคาดหวังว่าวัตถุ a_point ทั้งหมดจะได้รับการปรับให้เหมาะสมหากมีการเชื่อมโยงภายใน เช่นใส่ไว้ในเนมสเปซที่ไม่ระบุตัวตนและดูว่าเกิดอะไรขึ้น goo.gl/wNL0HC
Arvid

@dyp: แม้แต่การกำหนดคอนสตรัคเตอร์ก็ทำได้ก็ต่อเมื่อประเภทนั้นอยู่ภายใต้การควบคุมของคุณ ตัวอย่างเช่นคุณไม่สามารถทำเช่นนั้นstruct addrinfoหรือstruct sockaddr_inดังนั้นคุณจึงเหลืองานมอบหมายแยกจากการประกาศ
musiphil

2
@musiphil อย่างน้อยใน C ++ 14 โครงสร้างสไตล์ C เหล่านั้นสามารถตั้งค่าได้อย่างเหมาะสมในฟังก์ชัน constexpr เป็นตัวแปรโลคัลโดยใช้การกำหนดแล้วส่งกลับจากฟังก์ชันนั้น นอกจากนี้ประเด็นของฉันไม่ได้แสดงการใช้งานตัวสร้างทางเลือกใน C ++ ซึ่งอนุญาตให้มีการปรับให้เหมาะสม แต่แสดงให้เห็นว่าเป็นไปได้ที่คอมไพลเลอร์จะดำเนินการปรับให้เหมาะสมนี้หากรูปแบบของการเริ่มต้นแตกต่างกัน หากคอมไพลเลอร์ "ดีพอ" (กล่าวคือรองรับรูปแบบการเพิ่มประสิทธิภาพนี้) ก็ไม่น่าจะเกี่ยวข้องไม่ว่าคุณจะใช้ ctor หรือ initializers ที่กำหนดไว้หรืออย่างอื่น
dyp
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.