Điểm của g ++ -Wreorder là gì?


150

Tùy chọn g ++ -Wall bao gồm -Wreorder. Những gì tùy chọn này làm được mô tả dưới đây. Tôi không rõ ràng tại sao ai đó quan tâm (đặc biệt là đủ để bật cái này theo mặc định trong -Wall).

-Wreorder (chỉ C ++)
  Cảnh báo khi thứ tự khởi tạo thành viên được cung cấp trong mã không
  phù hợp với thứ tự mà chúng phải được thực hiện. Ví dụ:

    cấu trúc A {
      int i;
      int j;
      A (): j (0), i (1) {}
    };

  Trình biên dịch sẽ sắp xếp lại các trình khởi tạo thành viên cho i và j để
  khớp với lệnh khai báo của các thành viên, phát ra một cảnh báo cho điều đó
  hiệu ứng. Cảnh báo này được bật bởi -Wall.

2
Một số câu trả lời hay ở đây, nhưng một câu trả lời ngắn gọn trong trường hợp nó được mọi người quan tâm: g ++ có một lá cờ để coi đây là lỗi toàn diện:-Werror=reorder
Max Barraclough

Câu trả lời:


257

Xem xét:

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

Bây giờ iđược khởi tạo cho một số giá trị không xác định, không phải không.

Ngoài ra, việc khởi tạo icó thể có một số tác dụng phụ mà thứ tự là quan trọng. Ví dụ

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

80
Đây thực sự nên là ví dụ trong tài liệu.
Ben S

3
cảm ơn. Với hầu hết các loại của chúng tôi là các loại POD với các trình khởi tạo đơn giản, điều này đã không xảy ra với tôi. Ví dụ của bạn tốt hơn nhiều so với ví dụ thủ công g ++.
Peeter Joot

5
@ Giống như điều này là do trình biên dịch (gcc) của bạn khởi tạo các biến chưa được khởi tạo thành 0, nhưng đây không phải là thứ mà bạn nên phụ thuộc vào; i là 0 chỉ là tác dụng phụ của giá trị chưa biết đối với các biến chưa được khởi tạo là 0.
ethanwu10

2
@Yakk Đơn hàng là trang man-> SO trả lời. Đây là một kho lưu trữ của trang man từ năm 2007 liệt kê rõ ràng ví dụ này. Nhận xét được đánh giá cao từ Ben S là một ví dụ vui nhộn về việc ai đó cho rằng có một cái gì đó tồn tại mà không cần kiểm tra xem nó đã tồn tại chưa. web.archive.org/web/20070712184121/http://linux.die.net/man/1/...
KymikoLoco

3
@KymikoLoco Điều đó hoàn toàn sai. Ví dụ trong trang man là một từ OP (nơi iđược khởi tạo thành 1). Ở đây, iđược khởi tạo j, mà thực sự chứng minh một vấn đề.
jazzpi

42

Vấn đề là ai đó có thể thấy danh sách các trình khởi tạo thành viên trong hàm tạo và nghĩ rằng chúng được thực thi theo thứ tự đó (j trước, sau đó là i). Họ không, họ được thực hiện theo thứ tự các thành viên được xác định trong lớp.

Giả sử bạn đã viết A(): j(0), i(j) {}. Ai đó có thể đọc được điều đó và nghĩ rằng tôi kết thúc với giá trị 0. Không, bởi vì bạn đã khởi tạo nó bằng j, có chứa rác vì bản thân nó không được khởi tạo.

Cảnh báo nhắc nhở bạn viết A(): i(j), j(0) {}, hy vọng sẽ trông cá hơn rất nhiều.


Trông / mùi tanh thật! :) Chắc chắn mùi mã :) Cảm ơn lời giải thích rõ ràng của bạn là đúng cho điểm. :)
Sẽ

1
"... nhắc nhở bạn viết A (): i (j), j (0) {} ..." Tôi đề nghị rằng nó nhắc bạn sắp xếp lại các thành viên của lớp trong trường hợp cụ thể này.
2.718

18

Các câu trả lời khác đã cung cấp một số ví dụ hay để chứng minh cho tùy chọn cảnh báo. Tôi nghĩ rằng tôi sẽ cung cấp một số bối cảnh lịch sử. Người tạo ra C ++, Bjarne Stroustrup, giải thích trong cuốn sách Ngôn ngữ lập trình C ++ (ấn bản thứ 3, Trang 259):

Các hàm tạo của các thành viên được gọi trước khi phần thân của hàm tạo của lớp chứa được thực thi. Các constructor được gọi theo thứ tự mà chúng được khai báo trong lớp thay vì thứ tự chúng xuất hiện trong danh sách khởi tạo. Để tránh nhầm lẫn, tốt nhất là chỉ định các trình khởi tạo theo thứ tự khai báo. Các tàu khu trục được gọi theo thứ tự ngược lại của xây dựng.


10

Điều này có thể cắn bạn nếu người khởi tạo của bạn có tác dụng phụ. Xem xét:

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

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

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

Ở trên sẽ in "thanh" rồi "foo", mặc dù theo trực giác người ta sẽ cho rằng thứ tự đó được ghi trong danh sách khởi tạo.

Ngoài ra, nếu xythuộc một số loại do người dùng định nghĩa với hàm tạo, hàm tạo đó cũng có thể có tác dụng phụ, với cùng kết quả không rõ ràng.

Nó cũng có thể tự biểu hiện khi trình khởi tạo cho một thành viên tham chiếu thành viên khác.


7

Cảnh báo tồn tại bởi vì nếu bạn chỉ đọc hàm tạo, có vẻ như jđang được khởi tạo trước đó i. Điều này trở thành một vấn đề nếu một cái được sử dụng để khởi tạo cái khác, như trong

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

Khi bạn chỉ nhìn vào các nhà xây dựng, điều này có vẻ an toàn. Nhưng trong thực tế, jvẫn chưa được khởi tạo tại thời điểm nó được sử dụng để khởi tạo i, và do đó mã sẽ không hoạt động như mong đợi. Do đó cảnh báo.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.