Apa gunanya g ++ -Wreorder?

150

Opsi g ++ -Wall termasuk -Wreorder. Apa yang dilakukan opsi ini dijelaskan di bawah ini. Tidak jelas bagi saya mengapa seseorang peduli (terutama cukup untuk mengaktifkannya secara default di -Dinding).

-Wreorder (Khusus C ++)
  Peringatkan ketika urutan inisialisasi anggota yang diberikan dalam kode tidak
  cocok dengan urutan di mana mereka harus dieksekusi. Misalnya:

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

  Kompiler akan mengatur ulang inisialisasi anggota untuk i dan j menjadi
  cocok dengan perintah deklarasi anggota, memancarkan peringatan untuk itu
  efek. Peringatan ini diaktifkan oleh -Wall.
Peeter Joot
sumber
2
Beberapa jawaban yang baik di sini, tetapi singkat untuk berjaga-jaga jika itu menarik bagi siapa pun: g ++ memiliki bendera untuk memperlakukan ini sebagai kesalahan penuh:-Werror=reorder
Max Barraclough

Jawaban:

257

Mempertimbangkan:

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

Sekarang idiinisialisasi ke beberapa nilai yang tidak diketahui, bukan nol.

Atau, inisialisasi imungkin memiliki beberapa efek samping yang urutannya penting. Misalnya

A(int n) : j(n++), i(n++) { }
int3
sumber
80
Ini harus menjadi contoh dalam dokumentasi.
Ben S
3
Terima kasih. Dengan sebagian besar tipe kami menjadi tipe POD dengan inisialisasi sederhana ini tidak terjadi pada saya. Contoh Anda jauh lebih baik daripada contoh manual g ++.
Peeter Joot
5
@ Mike ini karena kompiler (gcc) Anda menginisialisasi variabel yang tidak diinisialisasi ke 0, tetapi ini bukan sesuatu yang harus Anda andalkan; saya menjadi 0 hanyalah efek samping dari nilai yang tidak diketahui untuk variabel tidak diinisialisasi adalah 0.
ethanwu10
2
@ Yakk Perintahnya adalah halaman manual-> JADI jawab. Berikut ini adalah arsip halaman manual dari 2007 yang mencantumkan contoh ini secara eksplisit. Komentar terunggul dari Ben S adalah contoh lucu dari seseorang yang menyarankan bahwa sesuatu itu ada tanpa mengeceknya. web.archive.org/web/20070712184121/http://linux.die.net/man/1/…
KymikoLoco
3
@KymikoLoco Itu benar-benar salah. Contoh di halaman manual adalah yang dari OP (di mana idiinisialisasi ke 1). Di sini, idiinisialisasi j, yang sebenarnya menunjukkan masalah.
jazzpi
42

Masalahnya adalah seseorang mungkin melihat daftar inisialisasi anggota di konstruktor, dan berpikir bahwa mereka dieksekusi dalam urutan itu (j pertama, lalu saya). Mereka tidak, mereka dieksekusi dalam urutan anggota didefinisikan di kelas.

Misalkan Anda menulis A(): j(0), i(j) {}. Seseorang mungkin membaca itu, dan berpikir bahwa saya berakhir dengan nilai 0. Tidak, karena Anda menginisialisasinya dengan j, yang berisi sampah karena itu sendiri belum diinisialisasi.

Peringatan itu mengingatkan Anda untuk menulis A(): i(j), j(0) {}, yang semoga terlihat jauh lebih mencurigakan.

Steve Jessop
sumber
Terlihat / memang berbau amis! :) Pasti berbau kode :) Terima kasih atas penjelasan Anda yang jelas. :)
Will
1
"... mengingatkan Anda untuk menulis A (): i (j), j (0) {} ..." Saya menyarankan agar mengingatkan Anda untuk menyusun ulang anggota kelas dalam kasus khusus ini.
2.718
18

Jawaban lain telah memberikan beberapa contoh bagus yang membenarkan pilihan untuk peringatan. Saya pikir saya akan memberikan beberapa konteks sejarah. Pencipta C ++, Bjarne Stroustrup, menjelaskan dalam bukunya The C ++ language programming (edisi ke-3, Halaman 259):

Konstruktor anggota dipanggil sebelum tubuh konstruktor kelas yang mengandung itu dijalankan. Konstruktor dipanggil dalam urutan di mana mereka dideklarasikan di kelas daripada urutan di mana mereka muncul dalam daftar penginisialisasi. Untuk menghindari kebingungan, yang terbaik adalah menentukan inisialisasi dalam urutan deklarasi. Penghancur anggota dipanggil dengan urutan terbalik konstruksi.

gkb0986
sumber
10

Ini dapat menggigit Anda jika inisialisasi Anda memiliki efek samping. Mempertimbangkan:

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

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

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

Di atas akan mencetak "bilah" lalu "foo", meskipun secara intuitif orang akan menganggap bahwa urutan seperti yang tertulis dalam daftar penginisialisasi.

Atau, jika xdan ydari beberapa tipe yang ditentukan pengguna dengan konstruktor, konstruktor itu mungkin juga memiliki efek samping, dengan hasil yang tidak jelas sama.

Itu juga dapat memanifestasikan dirinya ketika penginisialisasi untuk satu anggota referensi anggota lain.

Pavel Minaev
sumber
7

Peringatan ada karena jika Anda baru saja membaca konstruktor, sepertinya jsudah diinisialisasi sebelumnya i. Ini menjadi masalah jika satu digunakan untuk menginisialisasi yang lain, seperti pada

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

Ketika Anda hanya melihat konstruktor, ini terlihat aman. Namun pada kenyataannya, jbelum diinisialisasi pada titik di mana ia digunakan untuk menginisialisasi i, sehingga kode tidak akan berfungsi seperti yang diharapkan. Karena itu peringatan.

jalf
sumber