공식적으로 typename은 무엇입니까?


131

때로는 gcc템플릿 을 사용할 때 실제로 해독 할 수없는 오류 메시지가 나오는 것을 보았습니다 ... 특히, 겉보기에 올바른 선언으로 인해 typename키워드 앞에 접두사를 붙이면 마술처럼 사라진 매우 이상한 컴파일 오류가 발생하는 문제가 발생 했습니다. 선언 ... (예를 들어 지난 주에 다른 템플릿 클래스의 멤버로 두 개의 반복자를 선언하고 있었고이 작업을 수행해야했습니다) ...

이야기는 typename무엇입니까?


답변:


207

다음은 Josuttis 책의 인용문입니다.

뒤에 typename오는 식별자가 유형임을 지정하기 위해 키워드 가 도입되었습니다. 다음 예제를 고려하십시오.

template <class T>
Class MyClass
{
  typename T::SubType * ptr;
  ...
};

여기 typename에서이 SubType유형이 임을 명확히하는 데 사용됩니다 class T. 따라서 ptr유형에 대한 포인터 T::SubType입니다. 하지 않고 typename, SubType 정적 멤버 간주됩니다. 그러므로

T::SubType * ptr

SubType형식 의 값을와 곱하는 것 T입니다 ptr.


2
좋은 책. 한 번 읽은 다음 원하는 경우 참조로 유지하십시오.
deft_code

1
독창적 인 독자는 문법에 의해 멤버 선언을 위해 곱셈 표현식이 허용되지 않는다는 것을 알게 될 것입니다. 따라서 C ++ 20 은이를 필요typename하지 않습니다 (모두는 아니지만).
Davis Herring

설득하지 않았다. 템플릿이 인스턴스화되면, 아주 잘 T :: 하위 유형이 무엇인지 정의
kovarex

36

Stan Lippman의 BLog 게시물 은 다음과 같이 제안합니다.

Stroustrup 은 기존 클래스 키워드재사용하여 기존 프로그램을 손상시킬 수있는 새 키워드를 도입하는 대신 유형 매개 변수를 지정했습니다. 새로운 키워드는 고려되지 않은 것이 아니라 잠재적 인 중단으로 인해 필요하지 않은 것으로 간주되었습니다. 그리고 최대 ISO-C ++ 표준 때까지,이 유형 매개 변수를 선언 할 수있는 유일한 방법이었다.

기본적으로 Stroustrup은 다음과 같은 이유로 표준에서 변경되는 새 키워드를 도입하지 않고 클래스 키워드를 재사용했습니다.

주어진 예로서

template <class T>
class Demonstration {
public:
void method() {
    T::A *aObj; // oops …
     // …
};

언어 문법 T::A *aObj;은 산술 식으로 잘못 해석 되어 새로운 키워드가 도입됩니다.typename

typename T::A* a6;

컴파일러에게 후속 명령문을 선언으로 처리하도록 지시합니다.

키워드가 급여에 있었기 때문에 클래스 키워드를 재사용하기 로 한 원래 결정 으로 인한 혼란을 해결하지 않겠습니까 ?

우리 둘 다 가지고있는 이유

이 게시물을 살펴볼 수 있습니다. 확실히 도움이 될 것입니다.


예,하지만 typename기존 키워드 class를 같은 목적으로 사용할 수 있다면 왜 새로운 키워드가 필요한 가요?
Jesper

5
@ Jesper : Xenus의 대답이 혼란 스럽다고 생각합니다. typenameJosuttis를 인용하여 Naveen의 답변에 설명 된 것처럼 구문 분석 문제를 해결하는 데 필요했습니다. ( class이 곳에 at을 삽입해도 효과가 없을 것이라고 생각합니다 .)이 경우 새 키워드가 허용 된 후에 만 ​​템플릿 인수 선언 ( 또는 정의입니까? ) 에서도 허용 class되었습니다. 오해의 소지가 있습니다.
sbi

13

코드를 고려하십시오

template<class T> somefunction( T * arg )
{
    T::sometype x; // broken
    .
    .

불행히도 컴파일러는 심령 적이어야 할 필요가 없으며 T :: sometype이 형식 이름 또는 T의 정적 멤버를 참조하는지 여부를 알지 못 typename하므로 다음과 같이 사용 합니다.

template<class T> somefunction( T * arg )
{
    typename T::sometype x; // works!
    .
    .

6

소위 종속 유형 의 멤버 ( "템플리트 매개 변수에 종속"을 의미 함)를 참조하는 일부 상황에서 컴파일러는 결과 구조의 의미 적 의미를 모호하지 않게 추론 할 수 없습니다. (예 : 유형 이름, 데이터 멤버 이름 또는 다른 이름인지 여부). 그런 경우 컴파일러에 이름이 해당 종속 유형의 구성원으로 정의 된 유형 이름에 속한다고 명시 적으로 명시하여 상황을 명확하게해야합니다.

예를 들어

template <class T> struct S {
  typename T::type i;
};

이 예에서는 typename코드를 컴파일하는 데 필요한 키워드 입니다.

종속 유형의 템플리트 멤버 (예 : 템플리트를 지정하는 이름)를 참조하려는 경우에도 마찬가지입니다. template다르게 배치되어 있지만 키워드를 사용하여 컴파일러를 도와야합니다.

template <class T> struct S {
  T::template ptr<int> p;
};

경우에 따라 두 가지를 모두 사용해야 할 수도 있습니다.

template <class T> struct S {
  typename T::template ptr<int>::type i;
};

(구문이 올바르게 있다면).

물론 키워드의 다른 역할은 typename템플릿 매개 변수 선언에 사용됩니다.


자세한 배경 정보 는 C ++ typename 키워드에 대한 설명을 참조하십시오 .
Atafar

5

비밀은 템플릿이 일부 유형에 특화 될 수 있다는 사실에 있습니다. 이는 또한 여러 유형에 대해 완전히 다른 인터페이스를 정의 할 수 있음을 의미합니다. 예를 들어 다음과 같이 쓸 수 있습니다.

template<typename T>
struct test {
    typedef T* ptr;
};

template<>         // complete specialization 
struct test<int> { // for the case T is int
    T* ptr;
};

왜 이것이 유용하고 실제로 유용한 지 묻습니다. 그것은 실제로 쓸모없는 것처럼 보입니다. 그러나 예를 들어 마음에 걸릴 유형이 다른보다 완전히 다른 모습 들. 분명히 유형의 유형을 다른 유형으로 변경하지는 않지만 발생할 수는 있습니다.std::vector<bool>referenceTreference

이제이 템플릿을 사용하여 자신 만의 템플릿을 작성하면 어떻게됩니까 test? 이 같은

template<typename T>
void print(T& x) {
    test<T>::ptr p = &x;
    std::cout << *p << std::endl;
}

그것이 유형 이라고 기대 하기 때문에 당신에게 괜찮은 것 같습니다 test<T>::ptr. 그러나 컴파일러는 알지 못하며 실제로 표준에서 반대를 기대한다고 조언 test<T>::ptr합니다. 유형이 아닙니다. 컴파일러에게 typename이전 에 추가해야 할 것을 알려주려면 . 올바른 템플릿은 다음과 같습니다

template<typename T>
void print(T& x) {
    typename test<T>::ptr p = &x;
    std::cout << *p << std::endl;
}

결론 : typename템플릿에서 중첩 유형의 템플릿을 사용할 때마다 추가 해야합니다. 물론 템플릿의 템플릿 매개 변수가 해당 내부 템플릿에 사용 된 경우에만 해당됩니다.


5

두 가지 용도 :

  1. A와 template인수 키워드 (대신 class)
  2. typename키워드 식별자 (오히려 고정 부재 변수보다) 형태 인 것을 컴파일러에 지시
template <typename T> class X  // [1]
{
    typename T::Y _member;  // [2] 
}

4

모든 답변에서 typename키워드가 두 가지 경우에 사용 된다고 언급 한 것 같습니다.

a) 템플릿 유형 매개 변수를 선언 할 때 예 :

template<class T> class MyClass{};        // these two cases are
template<typename T> class MyNewClass{};  // exactly the same.

그들 사이에 차이점이 없으며 정확히 동일합니다.

b) 템플릿에 중첩 종속 유형 이름 을 사용하기 전에

template<class T>
void foo(const T & param)
{
   typename T::NestedType * value; // we should use typename here
}

사용하지 않으면 typename구문 분석 / 컴파일 오류가 발생합니다.

Scot Meyers 책 Effective C ++ 에서 언급했듯이 두 번째 경우에 추가하고 싶은 typename것은 중첩 종속 유형 이름 앞에 사용하는 예외가 있다는 것입니다 . 예외는 당신이 사용하는 경우이다 중첩 의존의 형태 이름을 A와 중 하나를 기본 클래스 또는에서 멤버 초기화 목록 , 당신은 사용하지 말아야 typename있다 :

template<class T>
class D : public B<T>::NestedType               // No need for typename here
{
public:
   D(std::string str) : B<T>::NestedType(str)   // No need for typename here
   {
      typename B<T>::AnotherNestedType * x;     // typename is needed here
   }
}

참고 : 사용하여 typename두 번째 경우에 (즉, 중첩 의존의 형태 이름 앞에) 20 ++ C 때문에 필요하지 않습니다.


2
#include <iostream>

class A {
public:
    typedef int my_t;
};

template <class T>
class B {
public:
    // T::my_t *ptr; // It will produce compilation error
    typename T::my_t *ptr; // It will output 5
};

int main() {
    B<A> b;
    int my_int = 5;
    b.ptr = &my_int;
    std::cout << *b.ptr;
    std::cin.ignore();
    return 0;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.