Skip to main content

Updated May 4, 2026

decltype

decltype deduces the type of an expression, preserving references and const qualifiers exactly.

Difference from auto

auto uses template deduction (drops ref/const), decltype preserves exact type including references.

Basic Usage​

int x = 42;
decltype(x) y = x; // int (same type as x)

const int& ref = x;
decltype(ref) r = x; // const int& (preserves reference and const)

auto a = ref; // int (drops reference and const)
decltype(ref) b = ref; // const int& (keeps everything)

decltype Rules​

Named Variables​

int x = 42;
const int cx = x;
int& rx = x;

decltype(x) // int
decltype(cx) // const int
decltype(rx) // int&

Rule 1: For named variables, decltype gives the declared type exactly.

Expressions​

int x = 42;

decltype((x)) // int& (parentheses make it expression)
decltype(x + 0) // int (expression result)
decltype(x++) // int (post-increment returns value)
decltype(++x) // int& (pre-increment returns reference)

Rule 2: For expressions (including parentheses around names), decltype gives value category:

  • lvalue expression → T&
  • xvalue expression → T&&
  • prvalue expression → T

Practical Uses​

Variable Type Matching​

std::vector<int> vec;

// Declare same type as existing variable
decltype(vec) vec2; // std::vector<int>
decltype(vec.size()) size; // size_t

// Useful when type is complex
std::map<std::string, std::vector<int>> map;
decltype(map) map2; // Exact same type

Return Type Deduction​

template<typename Container>
auto getElement(Container& c, int index) -> decltype(c[index]) {
return c[index];
}

// c[index] might return reference
std::vector<int> vec = {1, 2, 3};
auto& elem = getElement(vec, 0); // Returns int&

Perfect Return Type​

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

auto result = add(5, 3.14); // double (decltype(int + double))

decltype vs auto​

const int x = 42;
const int& ref = x;

// auto drops const and reference
auto a = ref; // int

// decltype preserves everything
decltype(ref) b = ref; // const int&

// auto with modifiers
auto& c = ref; // const int&
const auto d = ref; // const int

// decltype for exact type
decltype(ref) e = ref; // const int& (exact match)

Common Patterns​

Type of Member​

struct Point {
double x, y;
};

Point p;
decltype(p.x) value; // double

decltype(Point::x) coord; // double (static member access)

Container Element Type​

std::vector<int> vec;
decltype(vec[0]) elem; // int& (vector::operator[] returns reference)

std::vector<bool> bvec;
decltype(bvec[0]) proxy; // std::vector<bool>::reference (proxy)

Function Return Type​

int func();

decltype(func()) x; // int (return type of func)
decltype(func) f; // int() (function type)
decltype(&func) fp; // int(*)() (function pointer type)

Parentheses Matter!​

int x = 42;

decltype(x) // int (name)
decltype((x)) // int& (expression - lvalue)

// Why? (x) is an lvalue expression referring to x
// decltype of lvalue expression → reference
Critical Difference

decltype(name) ≠ decltype((name))
Parentheses change name to expression!


Return Type Deduction (C++11 vs C++14)​

C++11: Trailing Return Type​

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

Must use trailing return type because a and b not in scope before parameter list.

C++14: auto Return​

template<typename T, typename U>
auto add(T a, U b) {
return a + b; // auto deduction (uses template rules)
}

// Returns int for add(5, 10), drops reference
std::vector<int> vec;
auto getFirst() {
return vec[0]; // Returns int (copy), not int&
}

Combining auto and decltype​

std::vector<int> vec = {1, 2, 3};

// auto drops reference
auto x = vec[0]; // int (copy)

// decltype preserves reference
decltype(vec[0]) y = vec[0]; // int&

// decltype(auto) - best of both worlds (C++14)
decltype(auto) z = vec[0]; // int& (preserves return type)

Use Cases​

Generic Code​

template<typename Container>
void process(Container& c) {
// Get exact type of element
decltype(c[0]) elem = c[0];

// Modify if it's a reference
elem = 42; // Modifies container if elem is reference
}

Type Traits​

template<typename T>
struct remove_reference {
using type = T;
};

template<typename T>
struct remove_reference<T&> {
using type = T;
};

int x;
using Type = typename remove_reference<decltype(x)>::type; // int

Summary​

decltype:

  • Preserves exact type including references and const
  • decltype(name) → declared type
  • decltype(expression) → value category type
  • decltype((name)) → reference (expression)

Comparison:

const int& ref = x;

auto a = ref; // int (template deduction)
decltype(ref) b = ref; // const int& (exact type)
decltype(auto) c = ref; // const int& (C++14)

Use when:

  • Need exact type preservation
  • Template return type forwarding
  • Type of complex expressions
  • Generic code requiring perfect forwarding
// Common idioms
decltype(expr) var; // Match expression type
decltype(auto) var = expr; // Deduce with preservation (C++14)
auto func() -> decltype(expr) // Trailing return type