카테고리 보관물: C++

C++

공식적으로 typename은 무엇입니까? 사라진 매우 이상한 컴파일 오류가 발생하는

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

이야기는 typename무엇입니까?



답변

다음은 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.


답변

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;

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

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

우리 둘 다 가지고있는 이유

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


답변

코드를 고려하십시오

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!
    .
    .


답변

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

예를 들어

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템플릿 매개 변수 선언에 사용됩니다.


답변

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

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템플릿에서 중첩 유형의 템플릿을 사용할 때마다 추가 해야합니다. 물론 템플릿의 템플릿 매개 변수가 해당 내부 템플릿에 사용 된 경우에만 해당됩니다.


답변

두 가지 용도 :

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

답변

모든 답변에서 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 때문에 필요하지 않습니다.