정교화된 타입 지정자

정교화된 타입 지정자 (Elaborated Type Specifier)

이미 선언된 클래스 이름(class, struct, union)이나 열거형 이름을 가리킬 때, 그 이름이 비타입 선언에 가려져 있어도 참조할 수 있게 해주는 지정자예요. 그리고 새 클래스 이름을 선언할 때도 쓸 수 있어요. "이건 타입이지 변수가 아니야"라고 컴파일러에 말해주는 역할을 한다고 보면 돼요.

출처: cppreference

본문

문법

class-key class-name                                        (1)
enum enum-name                                             (2)
class-key attr (optional) identifier ;                     (3)
  • class-keyclass, struct, union 중 하나.
  • class-name — 이전에 선언된 클래스 타입의 이름(선택적으로 한정됨), 또는 아직 타입 이름으로 선언되지 않은 식별자.
  • enum-name — 이전에 선언된 열거형 타입의 이름(선택적으로 한정됨).
  • attr — (C++11 이후) 임의 개수의 속성.
  1. 클래스 타입을 위한 정교화된 타입 지정자.
  2. 열거형 타입을 위한 정교화된 타입 지정자.
  3. 정교화된 타입 지정자만으로 이루어진 선언은, 항상 그 선언을 포함하는 스코프에서 identifier가 이름인 클래스 타입을 선언해요.

불투명 열거형 선언(opaque enum declaration)은 형식 (3)과 비슷해 보이지만, 불투명 열거형 선언 후에는 그 열거형 타입이 완전한 타입이 돼요.

설명

형식 (3)은 정교화된 타입 지정자의 특수한 경우로, 보통 클래스의 **전방 선언(forward declaration)**이라고 불러요. 형식 (3)에 대한 설명은 전방 선언을 참고하고, 여기서는 형식 (1)과 (2)만 다룰게요.

정교화된 타입 지정자 안의 class-name이나 enum-name단순 식별자일 수도, **한정 식별자(qualified-id)**일 수도 있어요. 이름은 그 형태에 따라 비한정 이름 조회 또는 한정 이름 조회로 찾아봐요. 어느 쪽이든 비타입 이름은 고려하지 않아요.

class T
{
public:
    class U;
private:
    int U;
};

int main()
{
    int T;
    T t; // error: the local variable T is found
    class T t; // OK: finds ::T, the local variable T is ignored
    T::U* u; // error: lookup of T::U finds the private data member
    class T::U* u; // OK: the data member is ignored
}

이름 조회로 이전에 선언된 타입 이름을 찾지 못했는데, 정교화된 타입 지정자가 class, struct, union(즉 enum이 아님)으로 시작하고 class-name이 비한정 식별자라면, 이 정교화된 타입 지정자는 class-name클래스 선언이며, 대상 스코프는 가장 가까운 둘러싼 네임스페이스 또는 블록 스코프예요.

template<typename T>
struct Node
{
    struct Node* Next; // OK: lookup of Node finds the injected-class-name
    struct Data* Data; // OK: declares type Data at global scope
                       // and also declares the data member Data
    friend class ::List; // error: cannot introduce a qualified name
    enum Kind* kind; // error: cannot introduce an enum
};

Data* p; // OK: struct Data has been declared

이름이 typedef 이름, 타입 별칭, 템플릿 타입 매개변수, 또는 별칭 템플릿 특수화를 가리킨다면 프로그램은 ill-formed예요. 그렇지 않으면 정교화된 타입 지정자는 단순 타입 지정자가 자신의 type-name을 도입하는 것과 같은 방식으로 그 이름을 선언에 도입해요.

template<typename T>
class Node
{
    friend class T; // error: type parameter cannot appear in an elaborated type specifier;
                    // note that similar declaration `friend T;` is OK.
};

class A {};
enum b { f, t };

int main()
{
    class A a; // OK: equivalent to 'A a;'
    enum b flag; // OK: equivalent to 'b flag;'
}

정교화된 타입 지정자에 있는 class-key 또는 enum 키워드는 그 이름이 가리키는 선언의 종류와 일치해야 해요.

  • 열거형 타입(스코프 있는지 없는지와 무관)을 가리키려면 enum 키워드를 써야 해요.
  • union을 가리키려면 union class-key를 써야 해요.
  • 비-union 클래스 타입을 가리키려면 class 또는 struct class-key를 써야 해요 (여기서 classstruct는 서로 바꿔 쓸 수 있어요).
enum class E { a, b };
enum E x = E::a; // OK
enum class E y = E::b; // error: 'enum class' cannot introduce an elaborated type specifier

struct A {};
class A a; // OK

템플릿 인자로 쓰일 때, class T는 이름이 T타입 템플릿 매개변수를 뜻해요. 정교화된 타입 지정자로 도입되는, 타입이 T이름 없는 비타입 매개변수가 아니에요.

키워드

class,
struct,
union,
enum

참고 문헌

  • C++23 표준 (ISO/IEC 14882:2024): 6.5.6 Elaborated type specifiers [basic.lookup.elab], 9.2.9.4 Elaborated type specifiers [dcl.type.elab]
  • C++20 표준 (ISO/IEC 14882:2020): 6.5.4 Elaborated type specifiers [basic.lookup.elab], 9.2.8.3 Elaborated type specifiers [dcl.type.elab]
  • C++17 표준 (ISO/IEC 14882:2017): 6.4.4 Elaborated type specifiers [basic.lookup.elab], 10.1.7.3 Elaborated type specifiers [dcl.type.elab]
  • C++14 표준 (ISO/IEC 14882:2014): 3.4.4 Elaborated type specifiers [basic.lookup.elab], 7.1.6.3 Elaborated type specifiers [dcl.type.elab]
  • C++11 표준 (ISO/IEC 14882:2011): 3.4.4 Elaborated type specifiers [basic.lookup.elab], 7.1.6.3 Elaborated type specifiers [dcl.type.elab]
  • C++98 표준 (ISO/IEC 14882:1998): 3.4.4 Elaborated type specifiers [basic.lookup.elab], 7.1.5.3 Elaborated type specifiers [dcl.type.elab]

참고: 이 절은 불완전해요. 이유: 아마도 9.1 [class.name]/2-3의 내용을 cpp/language/class에서 가져와야 함.

더 알아보기 (Learn more)