your programing

"template"및 "typename"키워드를 어디에, 왜 넣어야합니까?

lovepro 2020. 9. 27. 13:34
반응형

"template"및 "typename"키워드를 어디에, 왜 넣어야합니까?


템플릿에서, 어디서, 왜 넣어해야합니까 typenametemplate의존 이름을? 어쨌든 종속 이름은 정확히 무엇입니까? 다음 코드가 있습니다.

template <typename T, typename Tail> // Tail will be a UnionNode too.
struct UnionNode : public Tail {
    // ...
    template<typename U> struct inUnion {
        // Q: where to add typename/template here?
        typedef Tail::inUnion<U> dummy; 
    };
    template< > struct inUnion<T> {
    };
};
template <typename T> // For the last node Tn.
struct UnionNode<T, void> {
    // ...
    template<typename U> struct inUnion {
        char fail[ -2 + (sizeof(U)%2) ]; // Cannot be instantiated for any U
    };
    template< > struct inUnion<T> {
    };
};

내가 가진 문제는 typedef Tail::inUnion<U> dummy줄에 있습니다. 나는 그것이 inUnion종속적 인 이름 이라고 확신하고 , VC ++는 그것에 질식하는 데 아주 옳습니다. 또한 template컴파일러에게 inUnion이 템플릿 ID임을 알리기 위해 어딘가에 추가 할 수 있어야한다는 것도 알고 있습니다 . 하지만 정확히 어디입니까? 그리고 inUnion이 클래스 템플릿이라고 가정해야 inUnion<U>합니까? , 함수가 아닌 유형의 이름을 지정해야합니까?


C ++ 프로그램을 구문 분석하기 위해 컴파일러는 특정 이름이 유형인지 여부를 알아야합니다. 다음 예는 다음을 보여줍니다.

t * f;

어떻게 파싱해야합니까? 많은 언어에서 컴파일러는 코드 줄이 수행하는 작업을 구문 분석하고 기본적으로 알기 위해 이름의 의미를 알 필요가 없습니다. 그러나 C ++에서 위의 내용 t은 의미 에 따라 크게 다른 해석을 내릴 수 있습니다 . 유형 인 경우 포인터 선언이 f됩니다. 그러나 유형이 아닌 경우 곱셈이됩니다. 따라서 C ++ 표준은 단락 (3/7)에서 다음과 같이 말합니다.

일부 이름은 유형 또는 템플릿을 나타냅니다. 일반적으로 이름이 발견 될 때마다 해당 이름이 포함 된 프로그램을 계속 구문 분석하기 전에 해당 이름이 이러한 엔티티 중 하나를 나타내는 지 여부를 확인해야합니다. 이를 결정하는 프로세스를 이름 조회라고합니다.

템플릿 유형 매개 변수를 참조하는 t::x경우 컴파일러는 이름이 참조 하는 것을 어떻게 알 수 t있습니까? x곱할 수있는 정적 int 데이터 멤버이거나 선언을 생성 할 수있는 중첩 된 클래스 또는 typedef 일 수 있습니다. 이름에이 속성이있는 경우 (실제 템플릿 인수가 알려질 때까지 조회 할 수 없음)이를 종속 이름 이라고합니다 (템플릿 매개 변수에 따라 "종속").

사용자가 템플릿을 인스턴스화 할 때까지 기다리는 것이 좋습니다.

사용자가 템플릿을 인스턴스화 할 때까지 기다렸다가 나중에의 실제 의미를 알아 봅시다 t::x * f;.

이것은 작동하고 실제로 가능한 구현 접근 방식으로 표준에 의해 허용됩니다. 이러한 컴파일러는 기본적으로 템플릿의 텍스트를 내부 버퍼에 복사하고 인스턴스화가 필요할 때만 템플릿을 구문 분석하고 정의에서 오류를 감지 할 수 있습니다. 그러나 템플릿 작성자가 만든 오류로 템플릿 사용자 (불쌍한 동료!)를 괴롭히는 대신 다른 구현에서는 인스턴스화가 발생하기 전에 가능한 한 빨리 템플릿을 확인하고 정의에 오류를 제공하도록 선택합니다.

따라서 컴파일러에게 특정 이름은 유형이고 특정 이름은 그렇지 않다는 것을 알려주는 방법이 있어야합니다.

"typename"키워드

대답은 : 우리는 컴파일러가이 구문을 분석하는 방법을 결정합니다. 경우 t::x종속 이름입니다, 우리는하여 접두사해야 할 typename어떤 방법으로 그것을 구문 분석하는 컴파일러를 말해. 표준은 (14.6 / 2)에서 말합니다.

템플릿 선언 또는 정의에 사용되며 템플릿 매개 변수에 종속 된 이름은 적용 가능한 이름 조회가 유형 이름을 찾거나 이름이 키워드 typename에 의해 규정되지 않는 한 유형 이름을 지정하지 않는 것으로 간주됩니다.

많은 이름이 있습니다 typename, 필요하지 않습니다 때문에 구조 자체를 분석하는 방법을 해당 이름 템플릿 정의에서 조회, 그림과 컴파일러 캔, -와 예를 들어 T *f;, 때 T타입 템플릿 매개 변수입니다. 그러나 t::x * f;선언이 되려면 typename t::x *f;. 키워드를 생략하고 이름이 유형이 아닌 것으로 간주되지만 인스턴스화가 유형을 나타내는 것을 발견하면 컴파일러에서 일반적인 오류 메시지를 내 보냅니다. 결과적으로 오류가 정의 시간에 제공되는 경우가 있습니다.

// t::x is taken as non-type, but as an expression the following misses an
// operator between the two names or a semicolon separating them.
t::x f;

구문은 typename정규화 된 이름 이전 만 허용 합니다. 따라서 정규화되지 않은 이름은 항상 형식을 참조하는 것으로 알려진 것으로 간주됩니다.

소개 텍스트에서 알 수 있듯이 템플릿을 나타내는 이름에도 비슷한 문제가 있습니다.

"템플릿"키워드

위의 초기 인용문과 표준이 템플릿에 대한 특수 처리를 어떻게 요구하는지 기억하십니까? 다음과 같은 순진한 예를 들어 보겠습니다.

boost::function< int() > f;

인간 독자에게는 분명해 보일 수 있습니다. 컴파일러에게는 그렇지 않습니다. boost::functionand 의 다음과 같은 임의의 정의를 상상해보십시오 f.

namespace boost { int function = 0; }
int main() { 
  int f = 0;
  boost::function< int() > f; 
}

그것은 실제로 유효한 표현입니다 ! 보다 작음 연산자를 사용 boost::function하여 0 ( int())과 비교 한 다음보다 큼 연산자를 사용하여 결과 boolf. 그러나 잘 알다시피 boost::function 실생활 에서는 템플릿이 있으므로 컴파일러는 다음을 알고 있습니다 (14.2 / 3).

이름 조회 (3.4)가 이름이 템플릿 이름임을 발견 한 후이 이름 뒤에 <이 오면 <는 항상 템플릿 인수 목록의 시작으로 간주되며 이름 뒤에 적은 이름이 붙지 않습니다. 연산자보다.

이제 우리는에서와 같은 문제로 돌아 왔습니다 typename. 코드를 구문 분석 할 때 이름이 템플릿인지 아직 알 수 없다면 어떻게해야합니까? template지정된대로 템플릿 이름 바로 앞에 삽입해야합니다 14.2/4. 이것은 다음과 같습니다.

t::template f<int>(); // call a function template

템플릿 이름은 a 이후 ::뿐만 아니라 a ->또는 .클래스 멤버 액세스 이후에도 발생할 수 있습니다. 여기에도 키워드를 삽입해야합니다.

this->template f<int>(); // call a function template

의존성

선반에 두꺼운 Standardese 책을 가지고 있고 정확히 내가 무엇에 대해 이야기했는지 알고 싶어하는 사람들을 위해 이것이 Standard에서 어떻게 지정되는지에 대해 조금 이야기하겠습니다.

템플릿 선언에서 일부 구문은 템플릿을 인스턴스화하는 데 사용하는 템플릿 인수에 따라 다른 의미를 갖습니다. 표현식은 다른 유형이나 값을 가질 수 있고, 변수는 다른 유형을 가질 수 있으며, 함수 호출은 결국 다른 함수를 호출 할 수 있습니다. 이러한 구성은 일반적으로 템플릿 매개 변수 의존 한다고합니다 .

표준은 구조가 종속적인지 여부에 따라 규칙을 정확하게 정의합니다. 논리적으로 다른 그룹으로 구분합니다. 하나는 유형을 포착하고 다른 하나는 표현식을 포착합니다. 표현식은 값 및 / 또는 유형에 따라 달라질 수 있습니다. 그래서 우리는 전형적인 예가 추가되었습니다.

  • 종속 유형 (예 : 유형 템플릿 매개 변수 T)
  • 값 종속 표현식 (예 : 유형이 아닌 템플릿 매개 변수 N)
  • 유형 종속 표현식 (예 : 유형 템플릿 매개 변수에 대한 캐스트 (T)0)

규칙의 대부분은 직관적이고 반복적으로 구축되어 있습니다 : 예를 들어, 같은 구성 유형은 T[N]경우에 따라 유형 N값에 의존하는 표현 또는 T종속 유형입니다. 이에 대한 자세한 내용은 (14.6.2/1종속 유형, (14.6.2.2)유형 종속 표현식 및 (14.6.2.3)종속 표현식에 대한 섹션 ) 에서 읽을 수 있습니다 .

종속 이름

표준은 종속 이름정확히 무엇인지에 대해 약간 불분명 합니다. 간단한 읽기 (최소한의 원칙)에서 종속 이름으로 정의되는 모든 것은 아래 함수 이름의 특수한 경우입니다. 그러나 인스턴스화 컨텍스트에서 명확하게 찾아 볼 필요가 있기 때문에 종속 이름도 필요합니다 (다행히도 C ++ 14 중반부터위원회는이 혼란스러운 정의를 수정하는 방법을 조사하기 시작했습니다).T::x

이 문제를 피하기 위해 나는 표준 텍스트의 간단한 해석에 의지했습니다. 종속 유형 또는 표현식을 나타내는 모든 구문 중에서 그 하위 집합이 이름을 나타냅니다. 따라서 이러한 이름은 "종속 이름"입니다. 이름은 다른 형식을 취할 수 있습니다. 표준은 다음과 같이 말합니다.

이름은 엔티티 또는 레이블을 나타내는 식별자 (2.11), operator-function-id (13.5), conversion-function-id (12.3.2) 또는 template-id (14.2)의 사용입니다 (6.6.4, 6.1)

식별자는 문자 / 숫자의 단순한 시퀀스이며 다음 두 개는 operator +and operator type형식입니다. 마지막 형식은 template-name <argument list>입니다. 이 모든 것은 이름이며 표준에서 일반적으로 사용되는 이름에는 이름을 조회해야하는 네임 스페이스 또는 클래스를 나타내는 한정자가 포함될 수 있습니다.

값 종속 식은 1 + N이름이 아니라 N있습니다. 이름 인 모든 종속 구성의 하위 집합을 종속 이름 이라고 합니다. 그러나 함수 이름은 템플릿의 다른 인스턴스화에서 다른 의미를 가질 수 있지만 안타깝게도이 일반 규칙에 따라 잡히지 않습니다.

종속 함수 이름

이 기사의 주요 관심사는 아니지만 여전히 언급 할 가치가 있습니다. 함수 이름은 별도로 처리되는 예외입니다. 식별자 함수 이름은 그 자체가 아니라 호출에 사용되는 형식 종속 인수 식에 따라 다릅니다. 예에서 f((T)0), f종속 이름입니다. 표준에서 이것은 (14.6.2/1).

추가 참고 및 예

충분한 경우 typenametemplate. 코드는 다음과 같아야합니다.

template <typename T, typename Tail>
struct UnionNode : public Tail {
    // ...
    template<typename U> struct inUnion {
        typedef typename Tail::template inUnion<U> dummy;
    };
    // ...
};

키워드 template가 항상 이름의 마지막 부분에 나타날 필요는 없습니다. 다음 예제와 같이 범위로 사용되는 클래스 이름 앞에 중간에 나타날 수 있습니다.

typename t::template iterator<int>::value_type v;

경우에 따라 아래에 설명 된대로 키워드가 금지됩니다.

  • 종속 기본 클래스의 이름에는 쓸 수 없습니다 typename. 주어진 이름이 클래스 유형 이름이라고 가정합니다. 이것은 기본 클래스 목록과 생성자 이니셜 라이저 목록의 이름 모두에 해당됩니다.

     template <typename T>
     struct derive_from_Has_type : /* typename */ SomeBase<T>::type 
     { };
    
  • using-declarations template에서는 마지막. 이후 에 사용할 수 없으며 ::C ++위원회 솔루션 작업을하지 말라고 말했습니다 .

     template <typename T>
     struct derive_from_Has_type : SomeBase<T> {
        using SomeBase<T>::template type; // error
        using typename SomeBase<T>::type; // typename *is* allowed
     };
    

C ++ 11

문제

C ++ 03의 규칙은 언제 필요 typename하고 template대체로 합리적이지만 공식화에는 한 가지 성가신 단점이 있습니다.

template<typename T>
struct A {
  typedef int result_type;

  void f() {
    // error, "this" is dependent, "template" keyword needed
    this->g<float>();

    // OK
    g<float>();

    // error, "A<T>" is dependent, "typename" keyword needed
    A<T>::result_type n1;

    // OK
    result_type n2; 
  }

  template<typename U>
  void g();
};

볼 수 있듯이, 우리는 동음 키워드를 필요로하는 경우에도 컴파일러 수 자체 밖으로 완벽하게 그림 A::result_type만 할 수있다 int(따라서 일종), 그리고 this->g유일한 멤버 템플릿이 될 수 g이상 (선언 된 경우에도 A그 것, 명시 적으로 어딘가를 전문 해당 템플릿 내의 코드에 영향을주지 않으므로 의미는 나중에 전문화되는 A!) 의 영향을받을 수 없습니다 .

현재 인스턴스화

상황을 개선하기 위해 C ++ 11에서는 유형이 둘러싸는 템플릿을 참조 할 때 언어가 추적합니다. 이를 알기 위해서는 자신의 이름 (위에서,, A) A<T>특정 형식의 이름을 사용하여 유형이 형성되어야합니다 ::A<T>. 이러한 이름으로 참조되는 유형은 현재 인스턴스화로 알려져 있습니다 . 이름이 형성되는 형태가 부재 / 중첩 클래스의 경우 현재의 모든 인스턴스이다 (그리고, 여러 가지 종류가있을 수 A::NestedClassA양 전류 인스턴스화이다).

이 개념을 기반으로 언어는 CurrentInstantiation::Foo, FooCurrentInstantiationTyped->Foo(예 :)가 현재 인스턴스화 인 클래스의 멤버이거나 해당 비 종속 기본 클래스 중 하나 인 것으로 확인 되면 현재 인스턴스화의A *a = this; a->Foo 모든 멤버 라고 말합니다. 즉시 이름 조회).

규정자가 현재 인스턴스화의 구성원 인 경우 키워드 typenametemplate이제 더 이상 필요하지 않습니다. 여기서 기억해야 할 요점 A<T>여전히 유형 종속 이름이라는 것입니다 (결국 유형 종속 이름 T이기도합니다). 그러나 A<T>::result_type유형으로 알려져 있습니다. 컴파일러는 이러한 종류의 종속 유형을 "마법처럼"조사하여이를 파악합니다.

struct B {
  typedef int result_type;
};

template<typename T>
struct C { }; // could be specialized!

template<typename T>
struct D : B, C<T> {
  void f() {
    // OK, member of current instantiation!
    // A::result_type is not dependent: int
    D::result_type r1;

    // error, not a member of the current instantiation
    D::questionable_type r2;

    // OK for now - relying on C<T> to provide it
    // But not a member of the current instantiation
    typename D::questionable_type r3;        
  }
};

인상적이지만 더 잘할 수 있습니까? 언어는 더 나아가 및 요구 구현이 다시 보이는 것을 D::result_type인스턴스화 할 때 D::f(이 정의시에 이미 그 의미를 찾을 경우에도). 이제 조회 결과가 다르거 나 결과가 모호한 경우 프로그램의 형식이 잘못되어 진단을 제공해야합니다. 우리가 정의 된 경우 발생하는 상상 C이 같은

template<>
struct C<int> {
  typedef bool result_type;
  typedef int questionable_type;
};

인스턴스화 할 때 오류를 포착하려면 컴파일러가 필요합니다 D<int>::f. 당신은 두 세계의 최고 얻을 그래서 : 당신이 의존 기본 클래스에 문제가 얻을 수 있다면 당신을 보호 조회 "지연", 또한 "즉시"조회가 해방 당신은에서 typenametemplate.

알 수없는 전문화

의 코드 D에서 이름 typename D::questionable_type은 현재 인스턴스화의 구성원이 아닙니다. 대신 언어 는 알 수없는 전문화구성원으로 표시합니다 . 특히, 이것은 당신이하고있는 경우 항상 DependentTypeName::FooDependentTypedName->Foo하고 중 종속 유형이 없는 현재의 인스턴스가 (이 경우 컴파일러는 포기하고 우리가 나중에 무엇을 볼 것이다 "라고 Foo이다) 또는 그것 입니다 현재 인스턴스와 이름이 그것 또는 비 종속 기본 클래스에서 발견되지 않았으며 종속 기본 클래스도 있습니다.

h위에 정의 된 A클래스 템플릿 내에 멤버 함수가 있으면 어떻게되는지 상상해보십시오.

void h() {
  typename A<T>::questionable_type x;
}

C ++ 03에서는 인스턴스화하는 유효한 방법이 없기 때문에 언어가이 오류를 포착 할 수있었습니다 A<T>::h(에 대한 인수에 관계없이 T). C ++ 11에서 언어는 이제 컴파일러가이 규칙을 구현하는 이유를 추가로 확인합니다. 때문에 A더 의존 기본 클래스이 없으며, A어떤 멤버를 선언 questionable_type, 이름 A<T>::questionable_type입니다 현재 인스턴스의 멤버 알 수없는 전문화의 구성원입니다. 이 경우 해당 코드가 인스턴스화 시간에 유효하게 컴파일 될 수있는 방법이 없어야합니다. 따라서 언어는 한정자가 현재 인스턴스화 인 이름이 알려지지 않은 전문화의 멤버도 아니고 현재 인스턴스화의 멤버도 아닌 것을 금지합니다. ,이 위반은 여전히 ​​진단 할 필요가 없습니다.)

예와 퀴즈

이 답변 에 대해이 지식을 시도 하고 위의 정의가 실제 예제에서 당신에게 적합한 지 확인할 수 있습니다 (해당 답변에서 약간 덜 자세하게 반복됩니다).

C ++ 11 규칙은 다음과 같은 유효한 C ++ 03 코드를 잘못된 형식으로 만듭니다 (C ++위원회에서 의도 한 것은 아니지만 수정되지 않을 것임).

struct B { void f(); };
struct A : virtual B { void f(); };

template<typename T>
struct C : virtual B, T {
  void g() { this->f(); }
};

int main() { 
  C<A> c; c.g(); 
}

이 유효한 C ++ 03 코드를 결합 할 this->fA::f인스턴스화 시간에 모든 것이 괜찮습니다. 그러나 C ++ 11은 즉시 바인딩하고 B::f인스턴스화 할 때 조회가 여전히 일치하는지 확인해야합니다. 인스턴스화 할 때 C<A>::g지배 규칙이 적용 조회 찾을 A::f대신.


머리말

이 게시물은 litb의 게시물에 대한 읽기 쉬운 대안 입니다.

기본 목적은 동일합니다. "언제?"에 대한 설명 그리고 왜?" typename하고 template적용해야합니다.


의 목적은 무엇 typenametemplate?

typenametemplate템플릿을 선언 할 때 이외의 상황에서 사용할 수 있습니다.

C ++ 에는 컴파일러가 이름을 처리하는 방법을 명시 적으로 알려야하는 특정 컨텍스트가 있으며 이러한 모든 컨텍스트에는 공통점이 있습니다. 최소한 하나의 template-parameter 에 의존합니다 .

해석에 모호성이있을 수있는 이러한 이름을 다음과 같이 참조합니다. " 종속 이름 ".

이 게시물은 종속 이름 과 두 키워드 간의 관계에 대한 설명을 제공합니다 .


SNIPPET은 1000 단어 이상을 말합니다.

다음 function-template 에서 무슨 일이 일어나고 있는지 자신, 친구 또는 고양이에게 설명하십시오. ( A ) 로 표시된 진술에서 무슨 일이 일어나고 있습니까?

template<class T> void f_tmpl () { T::foo * x; /* <-- (A) */ }


생각만큼 쉽지 않을 수 있습니다.보다 구체적으로 ( A ) 평가 결과는 template-parameter로 전달 된 유형의 정의에 크게 좌우 됩니다 T.

다른 Ts는 관련된 의미를 크게 변경할 수 있습니다.

struct X { typedef int       foo;       }; /* (C) --> */ f_tmpl<X> ();
struct Y { static  int const foo = 123; }; /* (D) --> */ f_tmpl<Y> ();


두 가지 다른 시나리오 :

  • ( C ) 에서와 같이 X 형식으로 함수 템플릿을 인스턴스화하면 x 라는 int 에 대한 포인터 선언을 갖게됩니다 .

  • ( D ) 에서와 같이 Y 유형으로 템플릿을 인스턴스화하면 ( A ) 대신 이미 선언 된 변수 x를 곱한 123의 곱을 계산하는 표현식으로 구성됩니다 .



근거

C ++ 표준은 적어도이 경우에는 우리의 안전과 웰빙을 중요하게 생각합니다.

구현이 잠재적으로 불쾌한 놀라움으로 고통받는 것을 방지하기 위해 표준 은 이름을 type-name 또는 템플릿 으로 취급하려는 의도 명시 적으로 명시 하여 종속 이름 의 모호성을 분류하도록 요구합니다. id .

아무 것도 언급되지 않으면 종속 이름 은 변수 또는 함수로 간주됩니다.



종속 이름을 처리하는 방법 ?

만약 이것이 할리우드 영화라면, 부양 가족 은 신체 접촉을 통해 확산되는 질병이 될 것이며, 즉시 호스트에 영향을 미치고 혼란스럽게 만듭니다. 아마도 잘못된 형식의 성능, erhm .. 프로그램으로 이어질 수있는 혼란.

의존-이름 입니다 어떠한 직접적, 또는 간접적으로하는에 따라 이름 템플릿 매개 변수 .

template<class T> void g_tmpl () {
   SomeTrait<T>::type                   foo; // (E), ill-formed
   SomeTrait<T>::NestedTrait<int>::type bar; // (F), ill-formed
   foo.data<int> ();                         // (G), ill-formed    
}

위 스 니펫 에는 4 개의 종속 이름이 있습니다.

  • E )
    • "type" 은, 및를 SomeTrait<T>포함하는 의 인스턴스화에 따라 다릅니다 T.
  • F )
    • 템플릿 ID"NestedTrait"SomeTrait<T>, 및에 종속됩니다 .
    • ( F ) 끝에있는 "type"NestedTrait 에 따라 달라지며 SomeTrait<T>, 및에 종속됩니다 .
  • G )
    • 멤버 함수 템플릿 처럼 보이는 "data"foo 유형이 의 인스턴스화에 의존하기 때문에 간접적으로 종속 이름 입니다 .SomeTrait<T>

컴파일러가 종속 이름 을 변수 / 함수로 해석 할 경우 ( E ), ( F ) 또는 ( G ) 명령문 중 어느 것도 유효 하지 않습니다 (앞서 언급 한 바와 같이 명시 적으로 달리 말하지 않으면 발생합니다).

해결책

g_tmpl유효한 정의 를 만들려면 컴파일러에게 ( E ) 형식 , 템플릿 ID형식 ( F ), 템플릿 ID ( G )를 예상한다고 명시 적으로 알려야합니다 .

template<class T> void g_tmpl () {
   typename SomeTrait<T>::type foo;                            // (G), legal
   typename SomeTrait<T>::template NestedTrait<int>::type bar; // (H), legal
   foo.template data<int> ();                                  // (I), legal
}

이름 이 유형을 나타낼 때마다 관련된 모든 이름type-names 또는 namespaces 여야합니다.이를 염두에두고 정규화 된 이름typename 의 시작 부분에 적용한다는 것을 쉽게 알 수 있습니다 .

template그러나 다음과 같은 결론에 도달 할 방법이 없기 때문에 이와 관련하여 다릅니다. "아, 이것은 템플릿입니다. 그러면이 다른 것도 템플릿이어야합니다 . " . 이것은 우리가 그렇게 취급하고 싶은 이름template 앞에 직접 적용한다는 것을 의미합니다 .



CAN I JUST 스틱 키워드 어떠한 이름의 프런트?

" 캔 난 그냥 스틱 typenametemplate... 어떤 이름 앞에 나는 그들이 나타나는 상황에 대해 걱정 싶지 않아? "-Some C++ Developer

표준의 규칙에 따르면 정규화 된 이름 ( K )을 다루는 한 키워드를 적용 할 수 있지만 이름이 정규화 되지 않은 경우 응용 프로그램의 형식이 잘못되었습니다 ( L ).

namespace N {
  template<class T>
  struct X { };
}

         N::         X<int> a; // ...  legal
typename N::template X<int> b; // (K), legal
typename template    X<int> c; // (L), ill-formed

참고 : 필요하지 않은 상황에서 적용 typename하거나 적용 template하는 것은 좋은 관행으로 간주되지 않습니다. 당신이 무언가를 할 수 있다고해서 그렇게해야한다는 의미는 아닙니다.


또한 컨텍스트가 typename하고 template있다 명시 적으로 허용되지는 :

  • 클래스가 상속하는베이스를 지정할 때

    파생 클래스의 기본 지정자 목록에 기록 된 모든 이름 은 이미 type-name 으로 처리되며 명시 적으로 지정 하면 형식typename 이 잘못되고 중복됩니다.

                       // .------- the base-specifier-list
     template<class T> // v
     struct Derived      : typename SomeTrait<T>::type /* <- ill-formed */ {
       ...
     };
    


  • 경우 템플릿 ID는 하나는 클래스의 파생에 언급되고 사용 지시어

     struct Base {
       template<class T>
       struct type { };
     };
    
     struct Derived : Base {
       using Base::template type; // ill-formed
       using Base::type;          // legal
     };
    

typedef typename Tail::inUnion<U> dummy;

그러나 inUnion 구현이 올바른지 확실하지 않습니다. 내가 올바르게 이해한다면이 클래스는 인스턴스화되지 않아야하므로 "실패"탭은 절대로 실패하지 않습니다. 단순한 부울 값으로 유형이 공용체에 있는지 여부를 나타내는 것이 더 나을 수 있습니다.

template <typename T, typename TypeList> struct Contains;

template <typename T, typename Head, typename Tail>
struct Contains<T, UnionNode<Head, Tail> >
{
    enum { result = Contains<T, Tail>::result };
};

template <typename T, typename Tail>
struct Contains<T, UnionNode<T, Tail> >
{
    enum { result = true };
};

template <typename T>
struct Contains<T, void>
{
    enum { result = false };
};

PS : Boost :: Variant 살펴보기

PS2 : 특히 Andrei Alexandrescu의 책 : Modern C ++ Design 에서 typelists 살펴보기


This answer is meant to be a rather short and sweet one to answer (part of) the titled question. If you want an answer with more detail that explains why you have to put them there, please go here.


The general rule for putting the typename keyword is mostly when you're using a template parameter and you want to access a nested typedef or using-alias, for example:

template<typename T>
struct test {
    using type = T; // no typename required
    using underlying_type = typename T::type // typename required
};

Note that this also applies for meta functions or things that take generic template parameters too. However, if the template parameter provided is an explicit type then you don't have to specify typename, for example:

template<typename T>
struct test {
    // typename required
    using type = typename std::conditional<true, const T&, T&&>::type;
    // no typename required
    using integer = std::conditional<true, int, float>::type;
};

The general rules for adding the template qualifier are mostly similar except they typically involve templated member functions (static or otherwise) of a struct/class that is itself templated, for example:

Given this struct and function:

template<typename T>
struct test {
    template<typename U>
    void get() const {
        std::cout << "get\n";
    }
};

template<typename T>
void func(const test<T>& t) {
    t.get<int>(); // error
}

Attempting to access t.get<int>() from inside the function will result in an error:

main.cpp:13:11: error: expected primary-expression before 'int'
     t.get<int>();
           ^
main.cpp:13:11: error: expected ';' before 'int'

Thus in this context you would need the template keyword beforehand and call it like so:

t.template get<int>()

That way the compiler will parse this properly rather than t.get < int.


I am placing JLBorges's excellent response to a similar question verbatim from cplusplus.com, as it is the most succinct explanation I've read on the subject.

In a template that we write, there are two kinds of names that could be used - dependant names and non- dependant names. A dependant name is a name that depends on a template parameter; a non-dependant name has the same meaning irrespective of what the template parameters are.

For example:

template< typename T > void foo( T& x, std::string str, int count )
{
    // these names are looked up during the second phase
    // when foo is instantiated and the type T is known
    x.size(); // dependant name (non-type)
    T::instance_count ; // dependant name (non-type)
    typename T::iterator i ; // dependant name (type)

    // during the first phase, 
    // T::instance_count is treated as a non-type (this is the default)
    // the typename keyword specifies that T::iterator is to be treated as a type.

    // these names are looked up during the first phase
    std::string::size_type s ; // non-dependant name (type)
    std::string::npos ; // non-dependant name (non-type)
    str.empty() ; // non-dependant name (non-type)
    count ; // non-dependant name (non-type)
}

What a dependant name refers to could be something different for each different instantiation of the template. As a consequence, C++ templates are subject to "two-phase name lookup". When a template is initially parsed (before any instantiation takes place) the compiler looks up the non-dependent names. When a particular instantiation of the template takes place, the template parameters are known by then, and the compiler looks up dependent names.

During the first phase, the parser needs to know if a dependant name is the name of a type or the name of a non-type. By default, a dependant name is assumed to be the name of a non-type. The typename keyword before a dependant name specifies that it is the name of a type.


Summary

Use the keyword typename only in template declarations and definitions provided you have a qualified name that refers to a type and depends on a template parameter.

참고URL : https://stackoverflow.com/questions/610245/where-and-why-do-i-have-to-put-the-template-and-typename-keywords

반응형