typeid 연산자
typeid 연산자
typeid 연산자는 어떤 타입의 정보를 조회할 때 써요. 다형적(polymorphic) 객체의 동적 타입을 알아야 할 때, 그리고 정적으로 타입을 식별해야 할 때 사용하죠. 상속받은 객체가 실제로는 어떤 파생 타입인지 런타임에 확인하고 싶을 때가 대표적인 상황이에요.
출처: cppreference
본문
문법
typeid( type ) (1)
typeid( expression ) (2)
typeid 표현식은 lvalue 표현식이에요. 다형적 타입 std::type_info의 const 한정 버전이나 그 타입에서 파생된 어떤 타입의 객체를, 정적 저장 기간(static storage duration)으로 가리키는 표현식이죠.
사용 지점에서 표준 라이브러리의 std::type_info 정의가 보이지 않으면 그 프로그램은 ill-formed예요.
설명
type을 나타내는std::type_info객체를 가리켜요.type이 참조 타입이면, 그 참조된 타입의 cv-한정이 없는 버전을 나타내는std::type_info객체를 가리켜요.expression을 조사해요.
expression이 다형적 타입(가상 함수를 하나라도 선언하거나 상속받는 클래스)의 객체를 식별하는 lvalue(C++11부터는 glvalue) 표현식이라면,typeid는 그 표현식을 평가한 뒤 그 표현식의 동적 타입을 나타내는std::type_info객체를 가리켜요.expression이 역참조(indirection) 표현식이고 그 피연산자가 널 포인터 값으로 평가되면,std::bad_typeid핸들러에 맞는 타입의 예외가 던져져요.- 그 외에는
typeid가 표현식을 평가하지 않고, 식별하는std::type_info객체가 그 표현식의 정적 타입을 나타내요. lvalue-to-rvalue, 배열-포인터, 함수-포인터 변환은 수행되지 않아요.
이때 type이나 expression의 타입이 클래스 타입이거나 클래스 타입에 대한 참조라면, 그 클래스 타입은 불완전 타입(incomplete type)이면 안 돼요.
| 하지만 prvalue 인자에는 (형식적으로) 임시 객체 생성(temporary materialization)이 수행돼요. 인자는 typeid 표현식이 나타난 문맥에서 파괴 가능해야 해요. | (since C++17) |
type이나 expression의 타입이 cv-한정이면, typeid의 결과는 cv-한정이 없는 타입을 나타내는 std::type_info 객체를 가리켜요(즉 typeid(const T) == typeid(T)).
typeid를 생성·소멸 중인 객체에 쓰면(소멸자나 생성자 안, 생성자 초기화 리스트·기본 멤버 초기화기 포함), 이 typeid가 가리키는 std::type_info 객체는 그 객체가 가장 파생된 클래스가 아니더라도 지금 생성·소멸되고 있는 클래스를 나타내요.
↑다른 문맥에서 이런 표현식을 평가하면 결과가 정의되지 않아요(undefined behavior).
참고
typeid를 다형적 타입의 표현식에 적용하면 평가에 런타임 오버헤드(가상 테이블 조회)가 생길 수 있어요. 그 외에는 typeid 표현식이 컴파일 시점에 결정돼요.
typeid가 가리키는 객체의 소멸자가 프로그램 종료 시 실행되는지 여부는 명시되지 않아요.
같은 타입에 대한 모든 typeid 표현식 평가가 같은 std::type_info 객체를 가리킬 거라는 보장은 없어요. 다만 비교가 같다고 나오고, 그 type_info 객체들의 std::type_info::hash_code와 std::type_index는 동일해요.
const std::type_info& ti1 = typeid(A);
const std::type_info& ti2 = typeid(A);
assert(&ti1 == &ti2); // not guaranteed
assert(ti1 == ti2); // guaranteed
assert(ti1.hash_code() == ti2.hash_code()); // guaranteed
assert(std::type_index(ti1) == std::type_index(ti2)); // guaranteed
| 기능 테스트 매크로 | 값 | 표준 | 기능 |
|---|---|---|---|
| __cpp_constexpr | 201907L | (C++20) |
상수 표현식에서의 다형적 typeid |
키워드
typeid
예제
아래 예제는 type_info::name이 전체 타입 이름을 돌려주는 구현에서의 출력을 보여줘요. gcc나 비슷한 컴파일러를 쓴다면 c++filt -t로 걸러 보세요.
#include <iostream>
#include <string>
#include <typeinfo>
struct Base {}; // non-polymorphic
struct Derived : Base {};
struct Base2 { virtual void foo() {} }; // polymorphic
struct Derived2 : Base2 {};
int main()
{
int myint = 50;
std::string mystr = "string";
double *mydoubleptr = nullptr;
std::cout << "myint has type: " << typeid(myint).name() << '\n'
<< "mystr has type: " << typeid(mystr).name() << '\n'
<< "mydoubleptr has type: " << typeid(mydoubleptr).name() << '\n';
// std::cout << myint is a glvalue expression of polymorphic type; it is evaluated
const std::type_info& r1 = typeid(std::cout << myint); // side-effect: prints 50
std::cout << '\n' << "std::cout<<myint has type : " << r1.name() << '\n';
// std::printf() is not a glvalue expression of polymorphic type; NOT evaluated
const std::type_info& r2 = typeid(std::printf("%d\n", myint));
std::cout << "printf(\"%d\\n\",myint) has type : " << r2.name() << '\n';
// Non-polymorphic lvalue is a static type
Derived d1;
Base& b1 = d1;
std::cout << "reference to non-polymorphic base: " << typeid(b1).name() << '\n';
Derived2 d2;
Base2& b2 = d2;
std::cout << "reference to polymorphic base: " << typeid(b2).name() << '\n';
try
{
// dereferencing a null pointer: okay for a non-polymorphic expression
std::cout << "mydoubleptr points to " << typeid(*mydoubleptr).name() << '\n';
// dereferencing a null pointer: not okay for a polymorphic lvalue
Derived2* bad_ptr = nullptr;
std::cout << "bad_ptr points to... ";
std::cout << typeid(*bad_ptr).name() << '\n';
}
catch (const std::bad_typeid& e)
{
std::cout << " caught " << e.what() << '\n';
}
}
가능한 출력:
======== output from Clang ========
myint has type: i
mystr has type: NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
mydoubleptr has type: Pd
50
std::cout<<myint has type : NSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
printf("%d\n",myint) has type : i
reference to non-polymorphic base: 4Base
reference to polymorphic base: 8Derived2
mydoubleptr points to d
bad_ptr points to... caught std::bad_typeid
======== output from MSVC ========
myint has type: int
mystr has type: class std::basic_string<char,struct std::char_traits<char>,
class std::allocator<char> >
mydoubleptr has type: double * __ptr64
50
std::cout<<myint has type : class std::basic_ostream<char,struct std::char_traits<char> >
printf("%d\n",myint) has type : int
reference to non-polymorphic base: struct Base
reference to polymorphic base: struct Derived2
mydoubleptr points to double
bad_ptr points to... caught Attempted a typeid of nullptr pointer!
더 알아보기
std::type_info는typeid연산자가 돌려주는 클래스로, 어떤 타입의 정보를 담고 있어요. 타입 정보 라이브러리 문서에서 자세히 볼 수 있어요.std::type_index는std::type_info객체를 감싸서 연관·비정렬 연관 컨테이너의 인덱스로 쓸 수 있게 해 주는 래퍼예요.- cppreference의 typeid 원문에서 결함 보고(defect report) 기록을 더 볼 수 있어요.