혼란스러운 템플릿 오류


91

나는 한동안 clang을 가지고 놀았고 템플릿 오류에서 복구하기위한 힌트를 제공해야하는 "test / SemaTemplate / dependent-template-recover.cpp"(clang 배포판)를 우연히 발견했습니다.

모든 것을 최소한의 예제로 쉽게 제거 할 수 있습니다.

template<typename T, typename U, int N> struct X {
    void f(T* t)
    {
        // expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}}
        t->f0<U>();
    }
};

clang에 의해 생성 된 오류 메시지 :

tpl.cpp:6:13: error: use 'template' keyword to treat 'f0' as a dependent template name
         t->f0<U>();
            ^
            template 
1 error generated.

...하지만 template코드 구문이 정확하도록 키워드를 정확히 삽입해야하는 위치를 이해하기가 어렵습니다 .


11
화살표가 가리키는 곳에 삽입 해 보셨나요?
Mike Seymour

3
마찬가지로
Prasoon Saurav

답변:


104

ISO C ++ 03 14.2 / 4 :

멤버 템플릿 전문화의 이름이 뒤에 나타날 때. 또는-> postfix-expression에서 또는 규정 된 ID의 중첩 이름 지정자 뒤에 있고 postfix-expression 또는 규정 된 ID가 템플릿 매개 변수 (14.6.2)에 명시 적으로 의존하는 경우 멤버 템플리트 이름은 다음과 같아야합니다. 키워드 template가 앞에 붙습니다 . 그렇지 않으면 이름이 템플릿이 아닌 이름으로 간주됩니다.

In t->f0<U>(); f0<U>->템플릿 매개 변수 뒤에 나타나고 명시 적으로 종속 U되는 멤버 템플릿 전문화이므로 멤버 템플릿 전문화는 template키워드 로 접두사로 지정해야합니다 .

그래서 변경 t->f0<U>()t->template f0<U>().


흥미롭게도 식을 괄호 안에 t->(f0<U>())넣는다 고 생각했습니다. f0<U>()독립형 식에 넣을 것이라고 생각

24
왜 이것이 사실인지에 대해 언급 해 주시겠습니까? C ++에 이런 종류의 구문이 필요한 이유는 무엇입니까?
호기심

2
그래 이거 이상해 언어는 템플릿 키워드가 있어야한다는 것을 "감지"할 수 있습니다. 그렇게 할 수 있다면 그 자체에 키워드를 "삽입"해야합니다.
Enrico Borba

26

다른 사람들이 지적한 것 외에도 때때로 컴파일러가 마음을 결정할 수 없으며 두 해석 모두 인스턴스화 할 때 대체 유효한 프로그램을 생성 할 수 있습니다.

#include <iostream>

template<typename T>
struct A {
  typedef int R();

  template<typename U>
  static U *f(int) { 
    return 0; 
  }

  static int f() { 
    return 0;
  }
};

template<typename T>
bool g() {
  A<T> a;
  return !(typename A<T>::R*)a.f<int()>(0);
}


int main() {
  std::cout << g<void>() << std::endl;
}

이 인쇄는 0생략 할 때 template전에 f<int()>그러나 1그것을 삽입 할 때. 코드가 무엇을하는지 알아 내기위한 연습으로 남겨 둡니다.


3
이제 그것은 사악한 예입니다!
Matthieu M.

1
Visual Studio 2013에서 설명하는 동작을 재현 할 수 없습니다. 항상을 호출 f<U>하고 항상 인쇄합니다 1. 나는 여전히 template키워드가 필요한 이유 와 그것이 어떤 차이를 만드는지 이해하지 못합니다 .
Violet Giraffe

@Violet VSC ++ 컴파일러는 호환 C ++ 컴파일러가 아닙니다. VSC ++가 항상 1을 인쇄하는 이유를 알고 싶다면 새로운 질문이 필요합니다.
Johannes Schaub-litb dec

1
이 답변 template은 왜 필요한지 설명합니다 . 이해하기 어려운 표준 용어에만 의존하지 않고 stackoverflow.com/questions/610245/… 해당 답변에서 여전히 혼란스러운 점이 있으면 신고 해주세요.
Johannes Schaub-litb 2014

@ JohannesSchaub-litb : 감사합니다, 훌륭한 답변입니다. 이미 내가 찬성했기 때문에 나는 그것을 전에 읽었습니다. 분명히 내 기억은 meh입니다.
Violet Giraffe

12

캐럿이있는 지점 바로 앞에 삽입하십시오.

template<typename T, typename U, int N> struct X {
     void f(T* t)
     {
        t->template f0<U>();
     }
};

편집 : 컴파일러처럼 생각하면이 규칙의 이유가 더 명확 해집니다. 컴파일러는 일반적으로 한 번에 하나 또는 두 개의 토큰 만 미리보고 나머지 표현식을 "미리보기"하지 않습니다. [편집 : 주석 참조] 키워드의 이유 typename는 종속 유형 이름을 나타 내기 위해 키워드가 필요한 이유와 같습니다 . 컴파일러에게 "보려는 식별자는 템플릿의 이름이 아니라 템플릿의 이름입니다. 보다 작음 기호가 뒤 따르는 정적 데이터 멤버의 이름 ".


1
나는 그것을 짐작할 수 없었을 것 입니다 ... 그러나 감사합니다 ;-). 분명히 항상 C ++에 대해 배울 것이 있습니다!

3
무한 미리보기를 사용하더라도 template. 유무에 관계없이 모두 template다른 동작을 가진 유효한 프로그램을 생성 하는 경우가 있습니다. 따라서 이것은 구문 문제 일뿐만 아니라 ( t->f0<int()>(0)보다 작음 및 템플릿 인수 목록 버전 모두에 대해 구문 적으로 유효합니다).
Johannes Schaub-litb

@Johannes Schaub-litb : 맞아요. 그래서 앞을 보는 것보다 표현에 일관된 의미 론적 의미를 할당하는 것이 더 문제입니다.
Doug

11

C ++ 템플릿 에서 발췌

.template Construct typename 도입 이후 매우 유사한 문제가 발견되었습니다. 표준 bitset 유형을 사용하는 다음 예제를 고려하십시오.

template<int N> 
void printBitset (std::bitset<N> const& bs) 
{ 
    std::cout << bs.template to_string<char,char_traits<char>, 
                                       allocator<char> >(); 
} 

이 예제에서 이상한 구조는 .template입니다. 템플릿을 추가로 사용하지 않으면 컴파일러는 뒤에 오는보다 작은 토큰 (<)이 실제로 "보다 작음"이 아니라 템플릿 인수 목록의 시작이라는 것을 알지 못합니다. 기간 이전의 구성이 템플릿 매개 변수에 의존하는 경우에만 문제가됩니다. 이 예에서 매개 변수 bs는 템플릿 매개 변수 N에 의존합니다.

결론적으로 .template 표기법 (및-> template과 같은 유사한 표기법)은 템플릿 내에서만 사용되어야하며 템플릿 매개 변수에 의존하는 것을 따르는 경우에만 사용해야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.