จุดประสงค์ของ g ++ -Wreorder คืออะไร


150

ตัวเลือก g ++ -Wall รวมถึง - คำสั่งซื้อ ตัวเลือกนี้ทำอะไรอธิบายไว้ด้านล่าง ไม่ชัดเจนสำหรับฉันว่าทำไมบางคนถึงสนใจ (โดยเฉพาะอย่างยิ่งพอที่จะเปิดใช้งานตามค่าเริ่มต้นใน -Wall)

- คำสั่งซื้อ (C ++ เท่านั้น)
  เตือนเมื่อลำดับของสมาชิกเริ่มต้นที่กำหนดในรหัสไม่ได้
  ตรงกับคำสั่งที่พวกเขาจะต้องดำเนินการ ตัวอย่างเช่น

    struct A {
      int i;
      int j;
      A (): j (0), i (1) {}
    };

  คอมไพเลอร์จะจัดเรียง initializers สมาชิกใหม่สำหรับ i และ j to
  ตรงกับคำสั่งประกาศของสมาชิกปล่อยเตือนไปที่
  ผล คำเตือนนี้เปิดใช้งานโดย -Wall

2
บางคำตอบที่ดีที่นี่ แต่สั้น ๆ ในกรณีที่เป็นที่สนใจของทุกคน: g ++ มีธงในการรักษานี้เป็นข้อผิดพลาดแบบเต็มเป่า:-Werror=reorder
Max Barraclough

คำตอบ:


257

พิจารณา:

struct A {
    int i;
    int j;
    A() : j(0), i(j) { }
};

ตอนนี้iเริ่มต้นเป็นค่าที่ไม่รู้จักบางอย่างไม่เป็นศูนย์

อีกวิธีหนึ่งการเริ่มต้นของiอาจมีผลข้างเคียงบางอย่างซึ่งเป็นเรื่องสำคัญ เช่น

A(int n) : j(n++), i(n++) { }

80
นี่ควรเป็นตัวอย่างในเอกสารประกอบ
Ben S

3
ขอบคุณ ด้วยประเภทของเราส่วนใหญ่เป็นประเภท POD ที่มี initializers ง่ายสิ่งนี้ไม่ได้เกิดขึ้นกับฉัน ตัวอย่างของคุณดีกว่าตัวอย่าง g ++ ด้วยตนเองมาก
Peeter Joot

5
@ ไมค์นี่เป็นเพราะคอมไพเลอร์ของคุณ (gcc) เริ่มต้นตัวแปร uninitialized เป็น 0 แต่นี่ไม่ใช่สิ่งที่คุณควรพึ่งพา ฉันเป็น 0 เป็นเพียงผลข้างเคียงของค่าที่ไม่รู้จักสำหรับตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้นคือ 0
ethanwu10

2
@Yakk คำสั่งซื้อคือ man page-> SO answer นี่คือไฟล์เก็บถาวรของ man page จาก 2007 ที่แสดงตัวอย่างนี้อย่างชัดเจน ความคิดเห็นที่ยกมาจาก Ben S เป็นตัวอย่างของคนที่แนะนำว่ามีบางสิ่งบางอย่างอยู่โดยไม่ได้ตรวจสอบว่ามันทำไปแล้ว web.archive.org/web/20070712184121/http://linux.die.net/man/1/…
KymikoLoco

3
@KymikoLoco นั้นผิดธรรมดา ตัวอย่างในหน้า man เป็นตัวอย่างจาก OP (โดยที่iเริ่มต้นได้ที่1) ที่นี่iจะเริ่มต้นได้jซึ่งแสดงให้เห็นถึงปัญหาจริง
jazzpi

42

ปัญหาคือใครบางคนอาจเห็นรายการของผู้เริ่มต้นสมาชิกในตัวสร้างและคิดว่าพวกเขากำลังดำเนินการตามลำดับ (j ก่อนแล้ว i) พวกเขาไม่ได้พวกเขาจะดำเนินการตามลำดับที่สมาชิกกำหนดไว้ในชั้นเรียน

A(): j(0), i(j) {}สมมติว่าคุณเขียน บางคนอาจอ่านมันและคิดว่าฉันลงท้ายด้วยค่า 0 มันไม่ได้เพราะคุณเริ่มต้นด้วย j ซึ่งมีขยะเพราะมันไม่ได้ถูกกำหนดค่าเริ่มต้น

คำเตือนเตือนให้คุณเขียนA(): i(j), j(0) {}ซึ่งหวังว่าจะดูคาวมากขึ้น


ดู / มีกลิ่นคาวแน่นอน! :) กลิ่นรหัสแน่นอน :) ขอบคุณสำหรับคำอธิบายที่ชัดเจนของคุณที่ตรงประเด็น :)
จะ

1
"... เตือนให้คุณเขียน A (): i (j), j (0) {} ... " ฉันแนะนำให้เตือนให้คุณเรียงลำดับสมาชิกของคลาสอีกครั้งในกรณีนี้
2.718

18

คำตอบอื่น ๆ ได้ให้ตัวอย่างที่ดีบางอย่างที่ปรับตัวเลือกสำหรับการเตือน ฉันคิดว่าฉันจะให้บริบททางประวัติศาสตร์บางอย่าง ผู้สร้าง C ++, Bjarne Stroustrup อธิบายในหนังสือของเขาภาษาการเขียนโปรแกรม C ++ (รุ่นที่ 3, หน้า 259):

ตัวสร้างของสมาชิกจะถูกเรียกใช้ก่อนที่จะดำเนินการเนื้อความของตัวสร้างคลาสที่มีอยู่ คอนสตรัคเตอร์จะถูกเรียกในลำดับที่พวกเขาถูกประกาศในชั้นเรียนมากกว่าลำดับที่พวกเขาปรากฏในรายการ initializer เพื่อหลีกเลี่ยงความสับสนที่ดีที่สุดคือการระบุ initializers ในลำดับการประกาศ destructors สมาชิกถูกเรียกในลำดับย้อนกลับของการก่อสร้าง


10

สิ่งนี้สามารถกัดคุณได้หากผู้เริ่มต้นมีผลข้างเคียง พิจารณา:

int foo() {
    puts("foo");
    return 1;
}

int bar() {
    puts("bar");
    return 2;
}

struct baz {
    int x, y;
    baz() : y(foo()), x(bar()) {}
};

ด้านบนจะพิมพ์ "บาร์" จากนั้น "foo" ถึงแม้ว่าโดยสังเขปอย่างใดอย่างหนึ่งจะถือว่าคำสั่งนั้นเขียนไว้ในรายการเริ่มต้น

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

นอกจากนี้ยังสามารถแสดงรายการตัวเองเมื่อ initializer สำหรับสมาชิกคนหนึ่งอ้างอิงสมาชิกอีกคน


7

คำเตือนที่มีอยู่เพราะถ้าคุณเพิ่งอ่านสร้างดูเหมือนว่าจะได้รับการเริ่มต้นก่อนj iปัญหานี้จะกลายเป็นปัญหาหากมีการใช้งานเพื่อเริ่มต้นอื่น ๆ เช่นเดียวกับใน

struct A {
  int i;
  int j;
  A(): j (0), i (this->j) { }
};

เมื่อคุณดูที่นวกรรมิกมันดูปลอดภัย แต่ในความเป็นจริงjยังไม่ได้เริ่มต้น ณ จุดที่ใช้ในการเริ่มต้นiและรหัสจะไม่ทำงานตามที่คาดไว้ ดังนั้นการเตือน

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