decltype, declval

MwG·2026년 3월 26일

C++

목록 보기
19/21

decltype

타입을 알고자 하는 식의 타입으로 치환되게 됨

int a = 9;
decltype(a) b = 2; // int

int &r_a = a;
decltype(r_a) r_b = b; // int&

int &&x = 3;
decltype(x) y = 2; // int&

괄호로 둘러쌓이지 않은 식별자 표현식(id-expression)일 경우 해당식의 타입을 얻을 수 있다.

식별자 표현식(id-expression)이란?

개별 엔티티(변수, 함수, 네임스페이스 등)를 식별하기 위해 사용하는 모든 명칭이 식별자 표현식에 해당한다.

주 식별자 표현식 (Primary id-expression)

변수명: int score = 100;에서 score

함수명: void move();에서 move

열거체 상수: enum Color { RED };에서 RED

정규화된 식별자 표현식 (Qualified id-expression)

범위 결정 연산자(::)를 사용하여 이름이 속한 위치를 명확히 밝힌 형태

네임스페이스 지정: std::cout, std::vector

클래스 멤버 지정: Player::Move, MyClass::value

전역 범위 지정: ::globalVar (전역 공간에 있는 이름을 명시적으로 가리킬 때)

만약 식별자 표현식이 아닌 다른 식을 전달할 경우?
ex) a + b
-> 값의 종류(value category)에 따라 달라지게 된다.

decltype의 쓰임

식의 값 종류가 xvalue일 경우 decltype 은 T && 가 된다.
식의 값 종류가 lvalue일 경우 decltype 은 T & 가 된다.
식의 값 종류가 prvalue일 경우 decltype 은 T 가 된다.

int a;
decltype((a)) b;

(a)는 식별자 표현식이 아님
이동 불가능 + &(a)형태로 적용가능 하므로 lvalue이다
즉, int가 아닌 int& 로 추론된다.

auto와 차이점

const 보존

const int& ref = x;

auto a = ref;       // a는 그냥 int (const와 &가 떨어져 나감 - 값 복사)
decltype(ref) d = ref; // d는 const int& (원래 타입 그대로 유지)

배열 그대로 전달

int arr[10];
auto arr2 = arr; //int *arr2 = arr;
decltype(arr) arr3; //int arr3[10];

즉, decltype은 auto와 다르게 원래 형태를 그대로 유지한다.

template<typename T, typename U>
void add(T t, U u, decltype(t + u)* result)
{
	*result = t + u;
}

만약 반환형을 decltype(t + u)로 할 경우엔 컴파일 오류가 발생하는데 t와 u의 정의가 decltype 보다 나중에 나오기 때문에(C++ 컴파일러는 코드를 위에서 아래로, 왼쪽에서 오른쪽으로 읽으며 해석) 함수의 리턴값을 인자들 정의 부분 나중에 두어야함.

template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u)
{
	*result = t + u;
}

declval

std::declval은 C++ 템플릿 프로그래밍(Template Meta-Programming, TMP)에서 "실제로 객체를 생성하지 않고도, 특정 타입의 객체가 있다고 가정하고 그 멤버나 연산 결과의 타입을 알아내기 위해" 사용하는 도구이다

std::declval()는 타입 T에 대한 우측값 참조(T&&)를 반환하는 함수 형태의 템플릿이다.
실제로 호출해서 실행할 수는 없고, 오직 decltype이나 sizeof 같은 컴파일 타임 연산 안에서만 쓰인다.

도입 이유?

어떤 타입의 멤버 함수 반환 타입을 알고 싶을 때, 그 타입에 기본 생성자가 없다면 문제가 생기게 된다.

struct Player {
  Player(int id) {} // 기본 생성자가 없음
  int getHp() { return 100; }
};

// Player의 getHp()가 뭘 반환하는지 알고 싶어서 이렇게 썼다면?
decltype(Player().getHp()) myHp; // 에러! Player()는 기본 생성자가 없어서 호출 불가.
#include <utility>

// 만약 Player 객체가 있다면, getHp()는 뭘 반환할지
decltype(std::declval<Player>().getHp()) myHp; // myHp는 int 타입

0개의 댓글