[2026] C++ decltype | Extract Expression Types

[2026] C++ decltype | Extract Expression Types

이 글의 핵심

decltype vs auto, decltype(auto), trailing return types, SFINAE with decltype, and the decltype(x) vs decltype((x)) pitfall for templates.

What is decltype?

decltype yields the type of an expression. Unlike plain auto in many cases, it can preserve top-level const and reference. 아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// 변수 선언 및 초기화
int x = 10;
decltype(x) y = 20;        // int
const int& ref = x;
decltype(ref) z = x;       // const int&

Key properties:

  • Unevaluated context (expression not executed)
  • Preserves cv-qualifiers and references
  • Different rules for id-expressions vs general expressions

auto vs decltype

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

const int& r = x;
auto a = r;           // int (decay: removes const and reference)
decltype(r) b = r;    // const int& (preserves everything)
int arr[5];
auto a2 = arr;        // int* (decay to pointer)
decltype(arr) a3;     // int[5] (preserves array type)

Comparison table:

Featureautodecltype
Removes constYesNo
Removes referenceYesNo
Array decayYesNo
Function decayYesNo

Trailing return types (C++11)

Before C++14, auto with trailing return was needed for dependent types: 아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// 실행 예제
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
    return a + b;
}
// Usage
auto result = add(1, 2.5);  // double

C++14 improvement: Plain auto with return type deduction: 다음은 간단한 cpp 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

template<typename T, typename U>
auto add(T a, U b) {
    return a + b;  // Type deduced automatically
}

decltype(auto) (C++14)

Combines auto deduction with decltype rules: 아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

int& getRef();
const int& getConstRef();
auto a = getRef();            // int (decay)
decltype(auto) b = getRef();  // int& (exact type)
auto c = getConstRef();            // int (decay)
decltype(auto) d = getConstRef();  // const int& (exact type)

Perfect return type forwarding

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

template<typename Func, typename....Args>
decltype(auto) call(Func&& f, Args&&....args) {
    // Returns exact type from f, including references
    return std::forward<Func>(f)(std::forward<Args>(args)...);
}
int& getInt();
decltype(auto) result = call(getInt);  // int&, not int

SFINAE with decltype

Detecting member functions

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <type_traits>
template<typename T>
auto process(T v) -> decltype(v.size(), void()) {
    std::cout << "Has size(): " << v.size() << "\n";
}
template<typename T>
void process(...) {
    std::cout << "No size()\n";
}
// Usage
std::vector<int> vec = {1, 2, 3};
process(vec);  // "Has size(): 3"
process(42);   // "No size()"

Using std::declval

아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <utility>
template<typename T>
auto getValue(T& container) 
    -> decltype(std::declval<T>()[0]) {
    return container[0];
}
// Works with any type that supports operator[]
std::vector<int> vec = {10, 20};
auto val = getValue(vec);  // int&

decltype(x) vs decltype((x))

Critical difference:

int x = 10;
decltype(x) a;      // int (id-expression: declared type)
decltype((x)) b = x; // int& (expression: lvalue reference)

Rules

ExpressionCategorydecltype result
x (id-expression)-Declared type
(x) (parenthesized)lvalueT&
std::move(x)xvalueT&&
42prvalueT

Real-world pitfall

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

template<typename T>
decltype(auto) forward(T& x) {
    return (x);  // ❌ Returns reference! Dangling if x is local
}
template<typename T>
decltype(auto) forward(T& x) {
    return x;  // ✅ Returns by value
}

Real-world applications

1. Generic lambda return type

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

auto lambda = [](auto x, auto y) -> decltype(x + y) {
    return x + y;
};
auto result1 = lambda(1, 2);      // int
auto result2 = lambda(1.5, 2);    // double
auto result3 = lambda(std::string("a"), std::string("b"));  // std::string

2. CRTP base class

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

template<typename Derived>
class Base {
public:
    decltype(auto) interface() {
        return static_cast<Derived*>(this)->implementation();
    }
};
class Derived : public Base<Derived> {
public:
    int& implementation() {
        static int value = 42;
        return value;
    }
};
Derived d;
int& ref = d.interface();  // Returns int&, not int

3. Perfect proxy

아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

template<typename T>
class Proxy {
    T* ptr_;
    
public:
    Proxy(T* p) : ptr_(p) {}
    
    template<typename....Args>
    decltype(auto) operator()(Args&&....args) {
        // Forwards exact return type from T::operator()
        return (*ptr_)(std::forward<Args>(args)...);
    }
};

Common mistakes

Mistake 1: Dangling reference with decltype(auto)

아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

decltype(auto) getLocal() {
    int x = 10;
    return (x);  // ❌ Returns int&, but x is destroyed!
}
// ✅ Fix: remove parentheses
decltype(auto) getLocal() {
    int x = 10;
    return x;  // Returns int (copy)
}

Mistake 2: Unnecessary decltype in variable declarations

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

int x = 10;
decltype(x) y = 20;  // ❌ Verbose, just use int
// ✅ Better
int y = 20;
// decltype useful when type is complex
std::vector<int> vec;
decltype(vec.begin()) it = vec.begin();  // ✅ Good use case

Mistake 3: Confusing decltype(auto) with auto&&

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

int x = 10;
auto&& a = x;              // int& (forwarding reference)
decltype(auto) b = x;      // int (copy)
decltype(auto) c = (x);    // int& (reference)

Type inspection at compile time

아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.

template<typename T>
struct TD;  // Type Displayer (intentionally undefined)
int x = 10;
TD<decltype(x)> xType;      // Error shows: TD<int>
TD<decltype((x))> xRefType; // Error shows: TD<int&>

Runtime type info

아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 코드를 직접 실행해보면서 동작을 확인해보세요.

#include <typeinfo>
#include <iostream>
int x = 10;
std::cout << typeid(decltype(x)).name() << "\n";      // "i" (int)
std::cout << typeid(decltype((x))).name() << "\n";    // "i" (reference removed)
// Better: use boost::core::demangle or compiler-specific

Performance implications

Zero overhead: decltype is purely compile-time. No runtime cost. 다음은 간단한 cpp 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// Both produce identical assembly
int x = 10;
int y = x;              // Direct
decltype(x) z = x;      // Via decltype

Advanced: decltype in SFINAE

Detecting operator overloads

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

template<typename T, typename U>
auto canAdd(int) -> decltype(std::declval<T>() + std::declval<U>(), std::true_type{});
template<typename T, typename U>
std::false_type canAdd(...);
// Usage
static_assert(decltype(canAdd<int, int>(0))::value);  // true
static_assert(!decltype(canAdd<int, std::string>(0))::value);  // false

Expression validity

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

template<typename T>
struct has_size {
private:
    template<typename U>
    static auto test(int) -> decltype(std::declval<U>().size(), std::true_type{});
    
    template<typename>
    static std::false_type test(...);
    
public:
    static constexpr bool value = decltype(test<T>(0))::value;
};
static_assert(has_size<std::vector<int>>::value);
static_assert(!has_size<int>::value);

C++20 improvements

Concepts replace many decltype SFINAE patterns

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// C++11-17: decltype SFINAE
template<typename T>
auto process(T v) -> decltype(v.size(), void());
// C++20: Concepts
template<typename T>
    requires requires(T v) { v.size(); }
void process(T v);

Compiler support

Compilerdecltypedecltype(auto)Notes
GCC4.3+4.9+Full support
Clang2.9+3.3+Excellent diagnostics
MSVC2010+2015+Some early bugs

Keywords

C++, decltype, decltype(auto), trailing return type, templates, type deduction, SFINAE, C++11, C++14

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3