etc./StackOverFlow

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

청렴결백한 만능 재주꾼 2023. 4. 30. 00:07
반응형

질문자 :MSalters


템플릿에서, 어디서, 왜 넣어해야합니까 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++가 그것에 질식하는 데 아주 옳다고 확신합니다.
또한 컴파일러에 inUnion이 템플릿 ID임을 알리기 위해 어딘가에 template 을 추가할 수 있어야 한다는 것도 알고 있습니다. 하지만 정확히 어디에? 그런 다음 inUnion이 클래스 템플릿이라고 가정해야 inUnion<U> 는 함수가 아니라 유형의 이름을 지정합니까?



( 내 C++11 답변도 여기 참조)

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

 t * f;

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

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

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

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

사용자가 템플릿을 인스턴스화할 때까지 기다렸다가 나중에 t::x * f; .

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

따라서 특정 이름은 유형이고 특정 이름은 유형이 아님을 컴파일러에 알리는 방법이 있어야 합니다.

"유형 이름" 키워드

대답은 : 우리는 컴파일러가이 구문을 분석하는 방법을 결정합니다. 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::xf;

구문은 정규화된 이름 typename 을 허용합니다. 따라서 정규화되지 않은 이름은 그렇게 하는 경우 항상 유형을 참조하는 것으로 알려져 있는 것으로 간주됩니다.

소개 텍스트에서 암시하는 것처럼 템플릿을 나타내는 이름에 대해 유사한 문제가 있습니다.

"템플릿" 키워드

위의 초기 인용문과 표준이 템플릿에 대한 특별한 처리를 요구하는 방식을 기억하십니까? 다음의 순진해 보이는 예를 들어보겠습니다.

 boost::function< int() > f;

인간 독자에게는 분명해 보일 수 있습니다. 컴파일러의 경우 그렇지 않습니다. boost::functionf 대한 임의의 정의를 상상해 보십시오.

 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)가 이름이 템플릿 이름임을 발견한 후 이 이름 뒤에 <가 있으면 <는 항상 템플릿 인수 목록의 시작으로 간주되며 이름 뒤에 less-가 붙지 않습니다. 연산자보다.

typename 과 동일한 문제로 돌아갑니다. 코드를 파싱할 때 이름이 템플릿인지 여부를 아직 알 수 없다면 어떻게 될까요? 14.2/4 지정된 대로 템플릿 이름 바로 앞에 template 을 삽입해야 합니다. 다음과 같습니다.

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

:: 뒤에 올 수 있을 뿐만 아니라 -> 또는 . 클래스 멤버 액세스에서. 거기에도 키워드를 삽입해야 합니다.

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

종속성

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

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

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

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

대부분의 규칙은 직관적이며 재귀적으로 작성됩니다. 예를 들어, N 이 값 종속 표현식이거나 T 가 종속 유형인 경우 T[N] 으로 구성된 유형은 종속 유형입니다. 이에 대한 자세한 내용은 종속 유형의 경우 (14.6.2/1 ), 유형 종속 표현식의 경우 (14.6.2.3) , 값 (14.6.2.2)

종속 이름

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

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

이름은 엔티티 또는 레이블(6.6.4, 6.1)

식별자는 단순한 문자/숫자의 시퀀스이며 다음 두 개는 operator +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 };

Johannes Schaub - litb

C++11

문제

typenametemplate 이 필요한 경우에 대한 C++03의 규칙은 대체로 합리적이지만 공식화에는 한 가지 성가신 단점이 있습니다.

 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_typeint (따라서 유형)만 될 수 this->g 가 나중에 선언된 g 수 있다는 것을 컴파일러가 완벽하게 파악하더라도 disambiguation 키워드가 필요합니다. ( 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::f 인스턴스화할 때 구현이 다시 D::result_type 조회하도록 요구 합니다(정의 시 이미 의미를 찾았더라도). 이제 조회 결과가 다르거나 결과가 모호하면 프로그램이 잘못된 형식이므로 진단을 받아야 합니다. C 과 같이 정의하면 어떻게 되는지 상상해 보세요.

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

D<int>::f 인스턴스화할 때 오류를 포착하려면 컴파일러가 필요합니다. 따라서 종속 기본 클래스에 문제가 발생할 수 있는 경우 보호하는 "지연된" 조회와 typenametemplate 에서 해방되는 "즉시" 조회의 두 가지 장점을 최대한 활용할 수 있습니다.

알 수 없는 전문 분야

D 코드에서 typename D::questionable_type 이름은 현재 인스턴스화의 구성원이 아닙니다. 대신 언어 는 알 수 없는 전문 분야 의 구성원으로 표시합니다. DependentTypeName::Foo 또는 DependentTypedName->Foo 를 수행하고 종속 유형이 현재 인스턴스화되지 않은 경우 항상 해당됩니다(이 경우 컴파일러는 포기하고 " Foo 가 무엇인지 나중에 살펴보겠습니다. ) 또는 현재의 인스턴스이며, 이름은 또는 비 의존 기본 클래스에서 찾을 수 없습니다와 종속 기본 클래스도 있습니다.

위에서 정의한 A 클래스 템플릿 h 가 있으면 어떻게 되는지 상상해 보세요.

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

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; cg(); }

이 유효한 C++03 코드는 this->fA::f 에 바인딩하고 모든 것이 정상입니다. 그러나 C++11은 즉시 이를 B::f 바인딩하고 인스턴스화할 때 조회가 여전히 일치하는지 확인하는 이중 확인이 필요합니다. C<A>::g 인스턴스화할 때 Dominance Rule이 적용되고 조회는 대신 A::f


Johannes Schaub - litb

머리말

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

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


typenametemplate 의 목적은 무엇입니까?

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

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

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

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


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

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

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


생각보다 쉽지 않을 수 있습니다. 특히 ( A ) T 로 전달된 유형의 정의에 크게 의존 합니다.

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

 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 또는 template- 로 취급하려는 모든 위치에서 의도를 명시적으로 명시 하여 종속 이름의 모호성을 분류하도록 명령합니다. 아이디

아무 것도 명시되지 않은 경우 종속 이름 은 변수 또는 함수로 간주됩니다.



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

이것이 헐리우드 영화 였다면, 부양명은 신체 접촉을 통해 퍼지는 질병이 될 것이고, 즉시 호스트에게 영향을 주어 혼란을 야기할 것입니다. 잘못된 형식의 perso-, 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개의 종속 이름이 있습니다.

  • )
    • "타입"의 인스턴스에 따라 SomeTrait<T> 등, T , 및;
  • )
    • template-id 인 "NestedTrait"SomeTrait<T> 에 의존하고;
    • (F)의 끝에서 "종류"에 따라 NestedTrait에 의존 SomeTrait<T> 와;
  • )
    • 멤버 함수 템플릿 처럼 보이는 "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 을 적용한다는 것을 의미합니다.



이름 앞에 키워드 를 붙일 수 있습니까?

" 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 을 적용하는 것은 좋은 방법으로 간주되지 않습니다. 당신이 무언가를 할 수 있다고 해서 당신이 해야 한다는 것을 의미하지는 않습니다.


typenametemplate명시적으로 허용되지 않는 컨텍스트가 있습니다.

  • 클래스가 상속하는 기반을 지정할 때

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

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

  • template-id 가 파생 클래스의 using 지시문에서 참조되는 경우

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

Filip Roséen - refp

이 답변은 제목이 붙은 질문(일부)에 답변하기 위한 다소 짧고 달콤한 답변입니다. 왜 거기에 넣어야 하는지를 더 자세히 설명하는 답변을 원하시면 여기 로 가십시오.


typename 키워드를 넣는 일반적인 규칙은 주로 템플릿 매개변수를 사용하고 중첩된 typedef 또는 using-alias에 액세스하려는 경우입니다. 예를 들면 다음과 같습니다.

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

이는 메타 함수나 일반 템플릿 매개변수를 사용하는 항목에도 적용됩니다. 그러나 제공된 템플릿 매개변수가 명시적 유형이면 typename 을 지정할 필요가 없습니다. 예를 들면 다음과 같습니다.

 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; };

template 한정자를 추가하기 위한 일반적인 규칙은 일반적으로 자체 템플릿화된 구조체/클래스의 템플릿화된 멤버 함수(정적 또는 기타)를 포함한다는 점을 제외하고는 대부분 유사합니다. 예를 들면 다음과 같습니다.

이 구조체와 함수가 주어지면:

 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 }

t.get<int>() 액세스를 시도하면 오류가 발생합니다.

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

따라서 이 컨텍스트에서는 template 키워드가 필요하고 다음과 같이 호출합니다.

t.template get<int>()

t.get < int 대신 이것을 적절하게 구문 분석합니다.


Rapptz

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 }; };

추신: Boost::Variant를 살펴보십시오.

PS2: 특히 Andrei Alexandrescu의 책: Modern C++ Design 에서 typelists를 살펴보십시오.


Luc Touraille

C++20 일명 C++2a

이 제안서에 설명된 대로 C++20 / C++2a는 typename 키워드에 대한 요구 사항을 더욱 완화했습니다. 특히, 구문적으로 유형만 유효한 모든 위치에서 이제 typename 따라서 알 수 없는 토큰이 유형이어야 하는 경우 C++20은 실제로 이를 유형으로 처리합니다. 그러나 이전 버전과의 호환성을 위해 typename 을 계속 사용할 수 있습니다.

특히, 대부분의 usingtypedef typename 없이 작성할 수 있습니다. typename 은 메서드 반환 형식 선언(후행 반환 형식 포함), 메서드 및 람다 매개 변수 선언, static_cast const_cast , dynamic_castreinterpret_cast 에 대한 형식 인수에서도 생략할 수 있습니다.

typename 이 여전히 필요한 한 가지 주목할만한 예외는 사용자 또는 라이브러리 정의 템플릿의 인스턴스화 인수 목록에 있습니다. 특정 인수가 유형으로 선언된 경우에도 typename 키워드는 여전히 필요합니다. 따라서 static_cast<A::B>(arg) 는 C++20에서 my_template_class<A::B>(arg) 는 A가 종속 범위이고 my_template_class 가 유형을 예상하는 경우 잘못된 형식입니다.

몇 가지 예:

 class A { public: typedef int type; static const int val { 1 }; }; class B { public: typedef float type; static const int val { 2 }; }; template<typename T> class C {}; template<int I> class D {}; template<typename T> class X { T::type v; // OK T::type f(T::type arg) { return arg; } // OK T::type g(double arg) { return static_cast<T::type>(arg); } // OK // C<T::type> c1; // error D<T::val> d; // OK (as has always been) C<typename T::type> c2; // OK (old style) typedef T::type mytype; // OK using mytypeagain = T::type; // OK C<mytype> c3; // OK (via typedef / using) }; X<A> xa; X<B> xb;

Kai Petzke

나는 cplusplus.com에서 비슷한 질문에 대한 JLBorges의 탁월한 답변을 그대로 옮겨 놓았습니다. 그것이 내가 주제에 대해 읽은 가장 간결한 설명이기 때문입니다.

우리가 작성하는 템플릿에는 종속 이름과 비종속 이름의 두 가지 이름을 사용할 수 있습니다. 종속 이름은 템플릿 매개변수에 따라 달라지는 이름입니다. 비종속적인 이름은 템플릿 매개변수가 무엇인지에 관계없이 동일한 의미를 갖습니다.

예를 들어:

 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) }

종속 이름이 참조하는 것은 템플릿의 서로 다른 인스턴스화마다 다를 수 있습니다. 결과적으로 C++ 템플릿은 "2단계 이름 조회"의 대상이 됩니다. 템플릿이 초기에 구문 분석될 때(인스턴스가 발생하기 전에) 컴파일러는 비종속적인 이름을 찾습니다. 템플릿의 특정 인스턴스화가 발생할 때 템플릿 매개변수는 그때까지 알려지고 컴파일러는 종속 이름을 찾습니다.

첫 번째 단계에서 파서는 종속 이름이 유형의 이름인지 아니면 비 유형의 이름인지 알아야 합니다. 기본적으로 종속 이름은 유형이 아닌 이름으로 간주됩니다. 종속 이름 앞의 typename 키워드는 그것이 유형의 이름임을 지정합니다.


요약

형식을 참조하고 템플릿 매개변수에 의존하는 정규화된 이름이 있는 경우에만 템플릿 선언 및 정의에서 키워드 typename을 사용하십시오.


KeyC0de

종속 이름은 이름이 템플릿 매개변수에 따라 달라집니다. 실제로 템플릿 클래스/함수를 시작하기 전에 템플릿 클래스/함수를 제대로 컴파일하려면 컴파일러에 지시해야 합니다.

  • typename -> 컴파일러에게 종속 이름이 실제 유형임을 알립니다.

     template <class T> struct DependentType { typename T::type a; using Type=typename T::type; };
  • 템플릿 -> 컴파일러에게 종속 이름이 템플릿 함수/클래스임을 알립니다.

     template <class T> struct DependentTemplate { // template function template <class U> static void func() {} // template class template <class U> struct ClassName{}; }; template <class T1, class T2> void foo() { // 3 ways to call a dependent template function DependentTemplate<T1>::template func<T2>(); DependentTemplate<T1>().template func<T2>(); (new DependentTemplate<T1>())->template func<T2>(); // You need both typename and template to reference a dependent template class typename DependentTemplate<T1>::template ClassName<T2> obj; using Type=typename DependentTemplate<T1>::template ClassName<T2>; }

baye

출처 : http:www.stackoverflow.com/questions/610245/where-and-why-do-i-have-to-put-the-template-and-typename-keywords

반응형