템플릿 및 이름 조회 이해


9

다음 코드 스 니펫을 이해하려고합니다.

스 니펫 # 1

template <typename T>
struct A
{
    static constexpr int VB = T::VD;
};

struct B : A<B>
{
};

gcc9 또는 clang9 모두 여기에 오류가 발생하지 않습니다.

Q.이 코드는 왜 컴파일됩니까? A<B>B에서 상속받을 때 인스턴스화하지 않습니까? B에는 VD가 없으므로 컴파일러에서 오류를 발생시키지 않아야합니까?

스 니펫 # 2

template <typename T>
struct A
{
    static constexpr auto AB = T::AD; // <- No member named AD in B
};

struct B : A<B>
{
    static constexpr auto AD = 0xD;
};

이 경우 gcc9는 정상적으로 컴파일되지만 clang9는 "B에 AD라는 멤버 없음"이라는 오류를 발생시킵니다.

Q. 왜 gcc9로 컴파일됩니까? 왜 clang9로 컴파일하지 않습니까?

스 니펫 # 3

template <typename T>
struct A
{
    using TB = typename T::TD;
};

struct B : A<B>
{
    using TD = int;
};

여기서 clang9와 gcc9는 모두 오류를 발생시킵니다. gcc9는 "불완전한 유형 'struct B'의 유효하지 않은 사용"을 말합니다.

Q. 여기서 struct B가 불완전한 경우 스 니펫 # 2에서 불완전하지 않은 이유는 무엇입니까?

사용 된 컴파일러 플래그 : -std=c++17 -O3 -Wall -Werror. 미리 감사드립니다 !!!


@xception이 아닌가요 struct B인스턴스 AB?
가변 부작용

clang9는 "B에 AD라는 멤버가 없음"이라는 오류를 표시 합니다. 같은 B.. 완전하지 않습니다 ...하지만 확실 멤버는 인스턴스화되어야 할 때
Jarod42

@MutableSideEffect 아, 내 나쁜, 템플릿으로도 읽어보십시오 :(
xception

@ Jarod42 그렇다면 gcc는 왜 잘 컴파일됩니까?
가변 부작용

1
나는이 질문을 "더 많은 초점을 필요로한다"고 표시했고, 그 질문에는 실제로 하나 이상의 질문 (따라서 나의 결론)이 포함되어 있는데 왜 내 깃발이 잘못 되었는가?
도미니크

답변:


4

나는 이것들이 본질적으로 [temp.inst] / 2 (강조 광산)로 귀결 된다고 믿는다 :

클래스 템플릿 특수화의 암시 적 인스턴스가 아니라 정의, 선언의 암시 적 인스턴스가 발생 , 기본 인수, 또는 noexcept - 지정자 클래스 멤버 함수, 멤버 클래스, 범위 멤버 열거, 정적 데이터 멤버 , 멤버 템플릿, 친구; […]

[temp.inst] / 9

구현은 그러한 인스턴스화가 요구되지 않는 한 클래스 템플릿의 정적 데이터 멤버 […]를 암시 적으로 인스턴스화해서는 안된다.

암시 적 템플릿 인스턴스화에 관한 표준의 표현은 많은 세부 사항을 해석에 공개합니다. 일반적으로, 당신은 단순히 템플릿의 일부에 의존 할 수없는 나에게 보일 것 없는 사양이 명시 적으로 말한다 않는 인스턴스화. 그러므로:

스 니펫 # 1

Q.이 코드는 왜 컴파일됩니까? B에서 상속받을 때 A를 인스턴스화하지 않습니까? B에는 VD가 없으므로 컴파일러에서 오류를 발생시키지 않아야합니까?

당신은 인스턴스화하고 A<B>있습니다. 그러나 인스턴스화 A<B>는 정적 데이터 멤버의 정의가 아닌 선언 만 인스턴스화합니다. VB정의가 있어야하는 방식으로 사용되지 않습니다. 컴파일러는이 코드를 승인해야합니다.

스 니펫 # 2

Q. 왜 gcc9로 컴파일됩니까? 왜 clang9로 컴파일하지 않습니까?

Jarod42가 지적한 바와 같이 선언 AB에는 자리 표시 자 유형 이 포함됩니다. 표준의 문구가 여기서 일어날 일에 대해 실제로 명확하지 않은 것 같습니다. 플레이스 홀더 유형을 포함하는 정적 데이터 멤버 선언의 인스턴스화가 플레이스 홀더 유형 공제를 트리거하여 정적 데이터 멤버의 정의가 필요한 사용을 구성합니까? 나는 표준에서 분명히 예 또는 아니오라고 말하는 문구를 찾을 수 없습니다. 따라서 두 해석이 모두 똑같이 유효하므로 GCC와 clang이 모두 옳습니다.

스 니펫 # 3

Q. 여기서 struct B가 불완전한 경우 스 니펫 # 2에서 불완전하지 않은 이유는 무엇입니까?

당신이 폐쇄에 도달 할 경우 클래스 유형이 시점에서 유일한 완료 }클래스 지정자 [class.mem] / 6 . 따라서 모든 스 니펫에서 B암시 적으로 인스턴스화하는 동안 불완전 A<B>합니다. 이것이 스 니펫 # 1과 관련이 없다는 것입니다. Snippet # 2에서 clang은 No member named AD in B결과적으로 오류 를 발생 시켰습니다 . Snippet # 2의 경우와 마찬가지로 정확히 멤버 별칭 선언이 인스턴스화 될 때 문구를 찾을 수 없습니다. 그러나 정적 데이터 멤버의 정의와 달리 클래스 템플리트의 암시 적 인스턴스화 중에 멤버 별명 선언의 인스턴스화를 명시 적으로 방지하기위한 문구는 없습니다. 따라서 GCC와 clang의 동작은이 경우 표준을 올바르게 해석 한 것입니다.


감사. 이 경우 본문 내에서 정적 데이터 멤버를 초기화합니다. 초기화의 선언 부분 또는 정적 데이터 멤버 정의 부분입니까? 초기화가 본문 내에 있으면 정적 데이터 멤버 선언의 일부라는 느낌이 들었습니다. 그것이 선언의 일부라면, 첫 번째 인용은 클래스 템플릿의 암시 적 인스턴스화의 일부로 즉각적인 인스턴스화가 필요합니다.
Johannes Schaub-litb

사양을 살펴본 결과 C ++ 14와 C ++ 17 사이에 차이점이있는 것으로 보입니다. C ++ 14에서 constexpr정적 데이터 멤버는 선언 일뿐입니다. C ++ 17은 inline변수를 constexpr얻었고을 암시 inline하며, 이는 체내 정적 데이터 멤버 선언을 정의로 만듭니다.
Johannes Schaub-litb

eel.is/c++draft/dcl.spec.auto#4.sentence-2 는 "자리 표시 자 형식을 사용하여 선언 된 변수의 형식은 초기화 프로그램에서 추론됩니다.이 선언은 초기화 선언에서 허용됩니다 ([dcl. init])로 변경합니다. ". 따라서 정적 데이터 멤버의 정의에는 이니셜 라이저가 포함되어 있으므로 정의가 필요하다고 주장합니다.
Johannes Schaub-litb

@ JohannesSchaub-litb 이것을 조사해 주셔서 감사합니다! 나는이 질문들에 대해서도 궁금했지만 결정적인 문구를 찾지 못했습니다. 초기화 대 정의에 관해서는 클래스 템플릿의 정의 내에서 멤버 함수의 정의를 고려하십시오. 이러한 정의는 또한 선언이며 다른 선언은 없습니다. 그러나 클래스 템플릿의 암시 적 인스턴스화는 선언 만 인스턴스화하지만 멤버 함수의 정의는 인스턴스화하지 않습니다. 정적 데이터 멤버에 대해 동일한 내용이 적용되지 않는 이유는 무엇입니까?
Michael Kenzel

정의 할 필요가없는 경우 정의가 인스턴스화되지 않습니다. 그러나이 auto경우 규칙은 선언이 초기화 선언이어야한다고 말합니다. 이것은 선언이 정의라는 것이 알려진 경우에만 가능합니다. 과거에는 비슷한 경우가 있었고 eel.is/c++draft/temp.inst#2.sentence-3 이 추가되었습니다. 여기서 정의 인 선언은 실제로 정의가없는 것으로 정의 된 것으로 인스턴스화됩니다. 정의를 인스턴스화.
Johannes Schaub-litb
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.