3 개의 주요 C ++ 컴파일러에서 다르게 컴파일되는 프로그램. 어느 것이 맞습니까?


116

이전 질문에 대한 흥미로운 후속 조치 (실제적으로 크게 중요하지는 않음) : 왜 C ++에서 변수를 선언 할 때 변수 이름을 괄호로 묶을 수 있습니까?

괄호 안의 선언과 삽입 된 클래스 이름 기능 을 결합하면 컴파일러 동작과 관련하여 놀라운 결과를 얻을 수 있다는 것을 알았습니다 .

다음 프로그램을 살펴보십시오.

#include <iostream>
struct B
{
};

struct C
{
  C (){ std::cout << "C" << '\n'; }
  C (B *) { std::cout << "C (B *)" << '\n';}
};

B *y = nullptr;
int main()
{
  C::C (y);
}
  1. g ++ 4.9.2로 컴파일하면 다음과 같은 컴파일 오류가 발생합니다.

    main.cpp:16:10: error: cannot call constructor 'C::C' directly [-fpermissive]
  2. MSVC2013 / 2015로 성공적으로 컴파일되고 인쇄됩니다. C (B *)

  3. clang 3.5로 성공적으로 컴파일되고 인쇄됩니다. C

그래서 어떤 질문이 옳은가? :)

(기술적으로 typedef로 유형을 변경 한 후 변수 선언을 중지하는 msvc 방법은 clang 버전으로 강력하게 흔들 렸습니다.)


3
C::C y;말이 안되죠? C::C (y); 처음에는 이것이 Most-Vexing-Parse stackoverflow.com/questions/tagged/most-vexing-parse 의 인스턴스라고 생각 하지 않았습니다 .하지만 이제는 세 컴파일러가 모두 "올바른"것을 의미하는 정의되지 않은 동작이라고 생각합니다.
Dale Wilson

4
# 3 그 소리는 # 2 MSVC 너무 관대하고 1 g을 ++ 오른쪽 ((내 생각)이며, 분명히 잘못된 것입니다

8
C::C유형의 이름을 지정하지 않고 함수 이름을 지정하므로 GCC가 맞습니다.
Galik


답변:


91

GCC는 적어도 C ++ 11 조회 규칙에 따르면 정확합니다. 3.4.3.1 [class.qual] / 2는 중첩 된 이름 지정자가 클래스 이름과 동일한 경우 삽입 된 클래스 이름이 아닌 생성자를 참조하도록 지정합니다. 예제를 제공합니다.

B::A ba;           // object of type A
A::A a;            // error, A::A is not a type name
struct A::A a2;    // object of type A

MSVC 가 생성자 매개 변수로 임시 C를 만드는 함수 스타일 캐스트 표현식으로 잘못 해석 한 것처럼 보입니다 y. Clang은이 y를 type 이라는 변수의 선언으로 잘못 해석합니다 C.


2
예, 3.4.3.1/2가 핵심입니다. 잘 했어!
궤도의 가벼운 경주

"함수 이름이 무시되지 않는 조회에서"라고 표시됩니다. 특히 주어진 예제 A::A a;에서 함수 이름을 무시해야하는 것 같습니다.
Columbo 2015

1
N4296의 번호 매기기에 따르면 키는 실제로 3.4.3.1/2.1입니다. "중첩 된 이름 지정자 뒤에 지정된 이름이 C에서 조회 될 때 C [...]의 삽입 된 클래스 이름 인 경우 이름은 대신 클래스 C의 생성자 이름으로 간주됩니다. " Mike의 요약은 약간 지나치게 단순화되어 있습니다. 예를 들어, 클래스 내 클래스 이름의 typedef는 클래스 이름과 다른 중첩 된 이름 지정자가 여전히 클래스 이름을 참조 할 수 있도록 허용합니다. ctor.
Jerry Coffin

2
@Mgetz : 질문에서 "MSVC2013 / 2015로 성공적으로 컴파일되고 인쇄됩니다 C (B *)" .
궤도의 가벼운 경주

2
완전성을 위해 이것은 진단이 필요한 상태로 잘못 형성되었는지 또는 진단이 필요하지 않은 상태로 잘못 형성되었는지를 명확히해야합니다. 후자의 경우 모든 컴파일러가 "올바른"것입니다.
MM

16

G ++는 오류를 제공하므로 정확합니다. 생성자는 new연산자 없이 이러한 형식으로 직접 호출 할 수 없기 때문 입니다. 코드가를 호출하더라도 C::C생성자 호출처럼 보입니다. 그러나 C ++ 11 표준 3.4.3.1에 따르면 이것은 합법적 인 함수 호출이나 유형 이름이 아닙니다 ( Mike Seymour의 답변 참조 ).

Clang은 올바른 함수를 호출하지 않기 때문에 잘못되었습니다.

MSVC는 합리적이지만 여전히 표준을 따르지 않습니다.


2
new운영자 는 무엇을 변경합니까?
Neil Kirk

1
@NeilKirk : new B(1,2,3)임시 인스턴스화 B(1,2,3)또는 선언 과 구별되는 일종의 "직접 생성자 호출"(물론 그렇지 않음) 이라고 생각하는 사람들을 위해 많이 B b(1,2,3)있습니다.
궤도의 가벼운 경주

@LightningRacisinObrit 무엇인지 어떻게 설명 new B(1,2,3)하시겠습니까?
user2030677

1
@ user2030677 : 키워드 new, 유형 이름 및 생성자 인수 목록을 사용하는 새 표현식 입니다. 여전히 "직접 생성자 호출"이 아닙니다.
궤도의 가벼운 경주

"Clang은 올바른 함수를 호출하지 않기 때문에 잘못되었습니다.": Clang C::C (y);C::C y;, 즉 C 유형의 변수 y 정의 (삽입 된 유형 C 사용 : : C는 점점 더 미친 언어 사양의 3.4.1,2를 잘못 무시하면서 C :: C를 생성자로 만듭니다). 당신이 생각하는 것처럼 그것은 눈에 띄는 오류가 아닙니다.
피터 - 분석 재개 모니카
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.