( 내 C ++ 11 답변도 여기를 참조 하십시오 )
C ++ 프로그램을 구문 분석하려면 컴파일러는 특정 이름이 유형인지 여부를 알아야합니다. 다음 예제는이를 보여줍니다.
t * f;
이것을 어떻게 파싱해야합니까? 많은 언어에서 컴파일러는 코드 행을 구문 분석하고 기본적으로 수행하는 작업을 이해하기 위해 이름의 의미를 알 필요가 없습니다. 그러나 C ++에서 위의 내용 t
은 의미 에 따라 크게 다른 해석을 생성 할 수 있습니다 . 타입이라면 포인터 선언이 될 것입니다 f
. 그러나 유형이 아닌 경우 곱셈이됩니다. 따라서 C ++ 표준은 (3/7) 단락에서 말합니다.
일부 이름은 유형 또는 템플릿을 나타냅니다. 일반적으로 이름이 발견 될 때마다 해당 이름이 포함 된 프로그램을 계속 구문 분석하기 전에 해당 이름이 이러한 엔티티 중 하나를 나타내는 지 여부를 판별해야합니다. 이를 결정하는 프로세스를 이름 조회라고합니다.
템플릿 유형 매개 변수를 참조하는 t::x
경우 컴파일러는 이름이 무엇을 의미 하는지 어떻게 알 수 t
있습니까? x
는 곱할 수 있거나 선언을 생성 할 수있는 중첩 클래스 또는 typedef 일 수있는 정적 int 데이터 멤버 일 수 있습니다. 이름에이 속성이있는 경우 실제 템플릿 인수를 알 때까지 조회 할 수없는 경우 해당 이름을 종속 이름 이라고합니다 (템플릿 매개 변수에 "의존").
사용자가 템플릿을 인스턴스화 할 때까지 기다리는 것이 좋습니다.
사용자가 템플릿을 인스턴스화 할 때까지 기다렸다가 나중에의 실제 의미를 찾으십시오 t::x * f;
.
이것은 작동하며 실제로 표준에서 가능한 구현 접근 방식으로 허용됩니다. 이 컴파일러는 기본적으로 템플릿의 텍스트를 내부 버퍼에 복사하고 인스턴스화가 필요한 경우에만 템플릿을 구문 분석하고 정의에서 오류를 감지합니다. 그러나 템플릿 제작자가 만든 오류로 템플릿 사용자 (가난한 동료)를 괴롭히는 대신, 다른 구현에서는 인스턴스화가 발생하기 전에 템플릿을 조기에 확인하고 가능한 한 빨리 정의 오류를 발생시킵니다.
따라서 컴파일러에게 특정 이름은 유형이고 특정 이름은 그렇지 않다고 알려주는 방법이 있어야합니다.
"typename"키워드
답은 : 우리 는 컴파일러가 이것을 어떻게 파싱해야하는지 결정합니다. 경우 t::x
종속 이름입니다, 우리는하여 접두사해야 할 typename
어떤 방법으로 그것을 구문 분석하는 컴파일러를 말해. 표준은 (14.6 / 2)에서 말합니다.
템플리트 선언 또는 정의에 사용되며 템플리트 매개 변수에 종속 된 이름은 적용 가능한 이름 검색에서 유형 이름을 찾거나 이름이 키워드 typename에 의해 규정되지 않는 한 유형의 이름을 지정하지 않는 것으로 간주됩니다.
typename
컴파일러는 템플릿 정의에서 적용 가능한 이름 조회를 사용하여 구문 자체를 구문 분석하는 방법 (예 : 유형 템플릿 매개 변수 T *f;
when 인 경우 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::function
및 의 다음과 같은 임의의 정의를 상상해보십시오 f
.
namespace boost { int function = 0; }
int main() {
int f = 0;
boost::function< int() > f;
}
실제로 유효한 표현입니다 ! 이 비교 연산자보다 적게 사용하여 boost::function
영 (대해 int()
) 다음 결과 비교해보다 큰 연산자를 사용 bool
대하여 f
. 그러나 잘 아시다시피, 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
의존성
선반에 두꺼운 표준 책이 있고 내가 정확히 무슨 말을하는지 알고 싶어하는 사람들을 위해, 나는 이것이 표준에서 어떻게 지정되는지에 대해 조금 이야기 할 것입니다.
템플릿 선언에서 일부 구문은 템플릿을 인스턴스화하는 데 사용하는 템플릿 인수에 따라 다른 의미를 갖습니다. 표현식에는 유형이나 값이 다르거 나 변수에 유형이 다르거 나 함수 호출에 따라 서로 다른 함수가 호출 될 수 있습니다. 이러한 구성은 일반적으로 템플릿 매개 변수 에 의존 한다고합니다 .
표준은 구조가 종속적인지 여부에 따라 정확하게 규칙을 정의합니다. 논리적으로 다른 그룹으로 분리합니다. 하나는 유형을, 다른 하나는 표현식을 포착합니다. 표현은 그 가치 및 / 또는 유형에 따라 달라질 수 있습니다. 따라서 일반적인 예제가 추가되었습니다.
- 종속 유형 (예 : 유형 템플리트 매개 변수
T
)
- 값 종속 표현식 (예 : 유형이 아닌 템플리트 매개 변수
N
)
- 유형 종속 표현식 (예 : 유형 템플리트 매개 변수로 캐스트
(T)0
)
규칙의 대부분은 직관적이고 반복적으로 구축되어 있습니다 : 예를 들어, 같은 구성 유형은 T[N]
경우에 따라 유형 N
값에 의존하는 표현 또는 T
종속 유형입니다. 이에 대한 자세한 내용은 (14.6.2/1
종속 유형, (14.6.2.2)
유형 종속 표현식 및 (14.6.2.3)
값 종속 표현식에 대해 섹션에서 읽을 수 있습니다 .
종속 이름
표준은 정확히 종속 이름 이 무엇인지에 대해 조금 불분명 합니다. 간단한 읽기 (최소한의 놀라움의 원리)에서 종속 이름으로 정의 된 모든 것은 아래 함수 이름의 특수한 경우입니다. 그러나 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)
있습니다.
추가 메모 및 예
충분한 경우에 typename
와 둘 다 필요합니다 template
. 코드는 다음과 같아야합니다
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
{ };
사용 선언 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
};