decltype, std::declval

하루공부·2024년 1월 20일
0

C++

목록 보기
16/25
post-thumbnail

C++ 아이콘 제작자: Darius Dan - Flaticon


decltype

  • C++11부터 decltype에 전달된 식이 괄호로 둘러쌓이지 않은 식별자 표현식(id-expression) 이라면 해당 식의 타입을 반환하는 함수를 제공

    식별자 표현식이란 변수의 이름, 함수의 이름, enum 이름, 클래스 멤버 수(a.b 나 a->b같은 꼴) 등을 의미한다.
    ==> 쉽게 생각하면 어떠한 연산을 하지 않고 단순히 객체 하나만을 가리키는 식

  • 그렇다면 만약에 decltype 에 식별자 표현식이 아닌 식을 전달하면 어떻게 될까??

    해당 식의 값의 종류(value category)에 다르다

    • 만일 식의 값 종류가 xvalue 라면 decltype 는 T&&
    • 만일 식의 값 종류가 lvalue 라면 decltype 는 T&
    • 만일 식의 값 종류가 prvalue 라면 decltype 는 T

int a, b;
decltype(a + b) c; // c 의 타입은?

==> a + b 는 prvalue 이므로 a + b 식의 실제 타입인 int 로 추론


int a;
decltype((a)) b; // b 의 타입은?

==> 우선 (a) 는 식별자 표현식이 아니다
==> 쉽게 생각하면 &(a) 와 같이 주소값 연산자를 적용할 수 있고, 당연히도 이동 불가능 이므로 lvalue되므로
==> int& 로 추론된다.


  • 근데 auto가 있는데 굳이 사용해야 하나?
  • 사실 auto가 정확하지 않다
const int i = 4;
auto j = i; // int j = i;
decltype(i) k = i; // const int k = i;

또 배열의 경우 auto 는 암시적으로 포인터로 변환하지만
decltype 의 경우 배열 타입 그대로를 전달한다

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

  • 또 decltype은 템플릿 함수에서 사용되는 상황이 있다.
    ==> 템플릿 함수에서 어떤 객체의 타입이 템플릿 인자들에 의해서 결정되는 경우가 있다.
template <typename T, typename U>
void add(T t, U u, /* 무슨 타입? */ result) {
	*result = t + u;
}

result의 타입이 t+u 결과에 의해 결정된다

여기에서 result 타입에 decltype을 사용하면 된다.

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

  • 위 예제에세 result에 값을 저장해서 반환하지 말고 더한 값은 바로 반환해보자.
template <typename T, typename U>
decltype(t + u) add(T t, U u) {
	return t + u;
}            // 컴파일 오류!!

컴파일러가 decltype에 있는 t와 u를 알지 못한다.
t와 u가 정의된 후에 사용해야 하기 때문이다.
하지만 C++ 14 부터 추가된 아래와 같은 문법으로 구현 가능

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

람다 함수와 비슷하게 생겼는데 리턴 타입에는 auto를 쓰고 -> 뒤에 실제 리턴 타입을 기입



std::declval

  • C++ 11 에서 새로 추가된 std::declval 함수 utilty 헤더 파일에 정의됨

만약 어떤 클래스의 멤버 함수의 리턴값을 알고싶어 decltype을 사용했다고 생각하자.
그런데 해당 함수에 생성자가 없으면 컴파일 오류가 발생할 것이다.
decltype(A().func()) what_type; ==> A() 생성자가 없으면 컴파일 오류 발생

  • std::declval 에 타입 T 를 전달하면, T 의 생성자를 직접 호출하지 않더라도 T 가 생성된 객체를 나타낼 수 있다.
  • 그리고 T타입에 생성자가 존재하지 않아도 T()를 한 것 같은 효과를 낸다.

declval 함수를 타입 연산에서만 사용해야지, 실제로 런타임에 사용하면 오류가 발생

B b = std::declval<B>();

참고로 C++ 14 부터는 함수의 리턴 타입을 컴파일러가 알아서 유추해주는 기능이 추가되었다.
이 경우 그냥 함수 리턴 타입을 auto 로 지정해주면 된다

template <typename T>
auto call_f_and_return(T& t) {
	return t.f();
}

공부한 내용 복습

개인 공부 기록용 블로그입니다.
틀린 부분 있으다면 지적해주시면 감사하겠습니다!!

0개의 댓글