g ++ -Wreorder의 요점은 무엇입니까?


150

g ++ -Wall 옵션은 -Wreorder를 포함합니다. 이 옵션의 기능은 다음과 같습니다. 왜 누군가가 관심을 가질 지 (특히 -Wall에서 이것을 기본값으로 설정하기에 충분할 것) 분명하지 않습니다.

-주문 (C ++ 만 해당)
  코드에 지정된 멤버 이니셜 라이저의 순서가 올바르지 않을 때 경고
  실행 순서와 일치해야합니다. 예를 들어 :

    구조체 A {
      int i;
      int j;
      A () : j (0), i (1) {}
    };

  컴파일러는 i와 j의 멤버 이니셜 라이저를
  멤버의 선언 순서와 일치하여 경고를 보냅니다.
  효과. 이 경고는 -Wall에 의해 활성화됩니다.

2
여기에 좋은 대답이 있지만, 누군가에게 흥미가있는 경우를 제외하고는 간단히 제쳐두고 : g ++에는이를 완전히 오류로 취급하는 플래그가 있습니다.-Werror=reorder
Max Barraclough

답변:


257

치다:

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

이제 i0이 아닌 알 수없는 값으로 초기화됩니다.

대안 적으로, 초기화 i는 순서가 중요한 부작용을 가질 수있다. 예 :

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

80
이것은 실제로 문서의 예제 여야합니다.
Ben S

3
감사. 대부분의 유형이 간단한 이니셜 라이저가있는 POD 유형이기 때문에 이것은 나에게 발생하지 않았습니다. 귀하의 예제는 g ++ 매뉴얼 예제보다 훨씬 낫습니다.
Peeter Joot 2009

5
@ 이것은 컴파일러 (gcc)가 초기화되지 않은 변수를 0으로 초기화하기 때문이지만, 이것은 의존 해야하는 것이 아닙니다. 나는 0이라는 것은 초기화되지 않은 변수들에 대한 알려지지 않은 값의
부작용

2
@ Yakk 순서는 맨 페이지-> SO 답변이었습니다. 다음은이 예제를 명시 적으로 나열한 2007 년 매뉴얼 페이지의 아카이브입니다. Ben S의 의견은 누군가가 이미 그것을 확인하지 않고도 무언가 존재한다고 제안하는 유쾌한 예입니다. web.archive.org/web/20070712184121/http://linux.die.net/man/1/...
KymikoLoco

3
@KymikoLoco 그건 그냥 잘못입니다. 매뉴얼 페이지의 예는 OP의 예입니다 (여기서 i초기화 됨 1). 여기서는 실제로 문제를 보여주는로 i초기화됩니다 j.
jazzpi

42

문제는 누군가 생성자에서 멤버 이니셜 라이저 목록을 볼 수 있고 순서대로 실행된다고 생각한다는 것입니다 (j 먼저, i). 멤버가 클래스에 정의 된 순서대로 실행되지 않습니다.

당신이 쓴다고 가정하자 A(): j(0), i(j) {}. 누군가 그것을 읽을 수 있고, 내가 0으로 끝났다고 생각할 것입니다. 초기화되지 않았기 때문에 정크를 포함하는 j로 초기화했기 때문에 그렇지 않습니다.

경고는을 작성하라는 메시지를 표시합니다 A(): i(j), j(0) {}.


실제로 비린내가 보이거나 냄새가 난다! :) 확실히 코드 냄새 :) 요점에 맞는 명확한 설명에 감사드립니다. :)
Will

1
"... A ()를 쓰라고 상기시킵니다 : i (j), j (0) {} ..."이 특별한 경우에 클래스 멤버를 재정렬하도록 상기시켜줍니다.
2.718

18

다른 답변은 경고 옵션을 정당화하는 좋은 예를 제공했습니다. 나는 역사적 맥락을 제시 할 것이라고 생각했다. C ++의 제작자 인 Bjarne Stroustrup은 그의 저서 C ++ 프로그래밍 언어 (3 판, 259 페이지)에서 다음과 같이 설명합니다.

포함하는 클래스 자체 생성자의 본문이 실행되기 전에 멤버의 생성자가 호출됩니다. 생성자는 이니셜 라이저 목록에 나타나는 순서가 아닌 클래스에서 선언 된 순서대로 호출됩니다. 혼동을 피하려면 이니셜 라이저를 선언 순서대로 지정하는 것이 가장 좋습니다. 멤버 소멸자는 반대 순서로 구성됩니다.


10

이니셜 라이저에 부작용이 있으면 물릴 수 있습니다. 치다:

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

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

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

위의 내용은 "bar"를 출력 한 다음 "foo"를 출력하지만, 직관적으로는 이니셜 라이저 목록에 순서가 기록되어 있다고 가정합니다.

또는 xy생성자가있는 사용자 정의 유형 인 경우 해당 생성자가 부작용이있을 수 있으며 결과는 동일합니다.

한 멤버의 이니셜 라이저가 다른 멤버를 참조 할 때도 나타날 수 있습니다.


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.