Injected-class-name

Injected-class-name (주입된 클래스 이름)

injected-class-name 은 어떤 클래스의 범위 안에서 그 클래스 자체를 가리키는 비한정 이름(unqualified name)이에요.

클래스 템플릿 안에서는 injected-class-name 을 두 가지로 쓸 수 있어요. 현재 템플릿을 가리키는 템플릿 이름으로 쓰거나, 현재 인스턴스화(current instantiation)를 가리키는 클래스 이름으로 쓸 수 있죠.

출처: cppreference

참고: cppreference 의 원래 페이지 주소는 https://en.cppreference.com/w/cpp/language/injected-class-name 이에요.

본문

설명 (Explanation)

클래스 범위 안에서, 현재 클래스의 클래스 이름 또는 현재 클래스 템플릿의 템플릿 이름은 마치 public 멤버 이름 인 것처럼 취급돼요. 이걸 injected-class-name 이라고 불러요. 이 이름의 선언 지점(point of declaration)은 클래스(템플릿) 정의의 여는 중괄호 바로 뒤예요.

int X;

struct X
{
    void f()
    {
        X* p;   // OK, X is an injected-class-name
        ::X* q; // Error: name lookup finds a variable name, which hides the struct name
    }
};

template<class T>
struct Y
{
    void g()
    {
        Y* p;    // OK, Y is an injected-class-name
        Y<T>* q; // OK, Y is an injected-class-name, but Y<T> is not
    }
};

다른 멤버들처럼 injected-class-name 도 상속돼요. private 또는 protected 상속이 있으면, 간접 기반 클래스의 injected-class-name 이 파생 클래스에서 접근 불가능하게 끝날 수 있어요.

struct A {};
struct B : private A {};
struct C : public B
{
    A* p;   // Error: injected-class-name A is inaccessible
    ::A* q; // OK, does not use the injected-class-name
};

클래스 템플릿 안에서 (In class template)

클래스 템플릿의 injected-class-name 은 템플릿 이름(template-name)이나 타입 이름(type-name)으로 쓸 수 있어요.

다음 경우에 injected-class-name 은 클래스 템플릿 자체의 템플릿 이름 으로 취급돼요.

  • <가 바로 뒤에 올 때
  • 템플릿 템플릿 인자(template template argument)로 쓰일 때
  • friend 클래스 템플릿 선언의 정교화된 클래스 지정자(elaborated class specifier)의 마지막 식별자일 때

그 외에는 타입 이름 으로 취급되고, 템플릿 이름 뒤에 클래스 템플릿의 템플릿 매개변수들을 <>로 감싼 것과 동등해요.

template<template<class, class> class>
struct A;

template<class T1, class T2>
struct X
{
    X<T1, T2>* p;   // OK, X is treated as a template-name
    using a = A<X>; // OK, X is treated as a template-name

    template<class U1, class U2>
    friend class X; // OK, X is treated as a template-name

    X* q;           // OK, X is treated as a type-name, equivalent to X<T1, T2>
};

클래스 템플릿 특수화 또는 부분 특수화의 범위 안에서, injected-class-name 을 타입 이름으로 쓰면 그 특수화 또는 부분 특수화의 템플릿 인자들을 <>로 감싼 템플릿 이름과 동등해요.

template<>
struct X<void, void>
{
    X* p; // OK, X is treated as a type-name, equivalent to X<void, void>

    template<class, class>
    friend class X; // OK, X is treated as a template-name (same as in primary template)

    X<void, void>* q; // OK, X is treated as a template-name
};

template<class T>
struct X<char, T>
{
    X* p, q; // OK, X is treated as a type-name, equivalent to X<char, T>

    using r = X<int, int>; // OK, can be used to name another specialization
};

클래스 템플릿 또는 클래스 템플릿 특수화의 injected-class-name 은 범위 안에 있는 어디서든 템플릿 이름이나 타입 이름으로 쓸 수 있어요.

template<>
class X<int, char>
{
    class B
    {
        X a;            // meaning X<int, char>

        template<class, class>
        friend class X; // meaning ::X
    };
};

template<class T>
struct Base
{
    Base* p; // OK: Base means Base<T>
};

template<class T>
struct Derived : public Base<T*>
{
    typename Derived::Base* p; // OK: Derived::Base means Derived<T>::Base,
                               // which is Base<T*>
};

template<class T, template<class> class U = T::template Base>
struct Third {};

Third<Derived<int>> t; // OK: default argument uses injected-class-name as a template

injected-class-name 을 찾아내는 룩업은 특정 경우(예: 둘 이상의 기반 클래스에서 발견될 때) 모호성을 낳을 수 있어요. 발견된 injected-class-name 들이 모두 같은 클래스 템플릿의 특수화를 가리키고, 이름을 템플릿 이름으로 쓴다면, 그 참조는 특수화가 아니라 클래스 템플릿 자체를 가리키며 모호하지 않아요.

template<class T>
struct Base {};

template<class T>
struct Derived: Base<int>, Base<char>
{
    typename Derived::Base b;         // error: ambiguous
    typename Derived::Base<double> d; // OK
};

injected-class-name 과 생성자 (injected-class-name and constructors)

생성자는 이름이 없지만, 둘러싼 클래스의 injected-class-name 은 생성자 선언과 정의에서 생성자를 가리키는 것으로 간주돼요.

한정 이름 C::D에서

  • 이름 룩업이 함수 이름을 무시하지 않고,
  • 클래스 C의 범위 안에서 D 룩업이 C의 injected-class-name 을 찾는다면,

그 한정 이름은 항상 C의 생성자를 가리키는 것으로 간주돼요. 그런 이름은 생성자의 선언(friend 생성자 선언, 생성자 템플릿 특수화, 생성자 템플릿 인스턴스화, 생성자 정의 등)에만 쓰이거나, 생성자를 상속받는 데에만 쓰일 수 있어요(C++11부터).

struct A
{
    A();
    A(int);

    template<class T>
    A(T) {}
};
using A_alias = A;

A::A() {}
A_alias::A(int) {}
template A::A(double);

struct B : A
{
    using A_alias::A;
};

A::A a;         // Error: A::A is considered to name a constructor, not a type
struct A::A a2; // OK, same as 'A a2;'
B::A b;         // OK, same as 'A b;'

결함 보고 (Defect reports)

동작 변경 결함 보고로 주요하게 조정된 내용은 이래요. injected-class-name 이 템플릿 템플릿 인자가 될 수 있게 되고 그 경우 클래스 템플릿 자신을 가리키게 됨(CWG 1004), 전체 template-id 가 아니라 템플릿 이름만 injected-class-name 이 될 수 있게 됨(CWG 2637) 등이에요.

더 알아보기 (Learn more)