decltype 키워드

decltype

  • 식별자 표현식이 아닌 식을 인자로 받고, 그 타입을 T라고 했을 때
    1. 식의 값이 xvalue인 경우 -> T&& 를 리턴
    2. 식의 값이 lvalue인 경우 -> T& 를 리턴
    3. 식의 값이 prvalue인 경우 -> T 를 리턴
  • decltype 안에 식이 존재하더라도, 컴파일 타임에 전부 식으로 치환
    • 식은 실행되지 않는다
int a, b;
decltype(a+b) c; // c -> int 타입

// &(A)로 주솟값 추론 가능
// (A)의 값 이동 불가능
// lvalue
int A;
decltype((A)) B; // B -> int& 타입

쓰임새

  • 대체재일 수 있는 auto
    • 그러나 auto는 타입을 정확하게 변형해주지 않는다
  • decltype은 정확하게 해준다
// ex1
const int i = 4;
auto j = i;         // int j = i;
decltype(i) k = i;  // const int k = 4;

// ex2
int arr[4];
auto arr2 = arr;    // int* arr2 = arr;
decltype(arr) arr3; // int arr3[4];
  • 탬플릿 함수에서 결과값의 타입이 달라지는 경우
// * 아래의 식은 컴파일 에러를 발생한다
// T t 와 U u 이전에 t+u가 나오기 때문 
template <typename T, typename U>
decltype(t + u)* add (T t, U u)
{
  // T가 int이고 U가 double인 경우, 리턴 타입은 double*가 된다
  return t + u;
}

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

std::declval

// 어떤 클래스 T는 (= B)
// 가본 생성자를 선언하지 않았다
template <typename T>
decltype (T().f()) func(T& t)
{
  return t.f();
}

// A는 컴파일러에 의해 자동 생성
struct A
{
  int f() {return 0;}
}

// B는 copy constructor에 의해 자동생성되지 않음
struct B
{
  B (int x) {}
  int f() {return 0;}
}

int main() {
  A a;
  B b(1);

  func(a);  // ok
  func(b);  // BAD
}
#include <utility>

// 어떤 클래스 T는 (= B)
// 여전히 가본 생성자를 선언하지 않았고
// 우리는 std::declval을 사용한다
template <typename T>
decltype (std::declval<T>().f()) func(T& t)
{
  return t.f();
}

struct A
{
  int f() {return 0;}
}

struct B
{
  B (int x) {}
  int f() {return 0;}
}

int main() {
  A a;
  B b(1);

  func(a);  // ok
  func(b);  // ok
}

별첨

  • C++14부터 리턴 타입에 auto를 써 컴파일러가 타입을 추측하는 기능이 추가
template <typename T>
auto func(T& t) {
  return t.f();
}

출처

Categories: ,

Updated: