auto and decltype — C++

auto — Type Deduction (C++11)

auto tells the compiler to deduce the type from the initializer. Reduces verbosity and keeps code in sync with type changes.

Variable examples:

auto x = 42;            // int
auto pi = 3.14;         // double
auto s = std::string{"hello"}; // std::string
auto it = vec.begin();  // std::vector<int>::iterator

Return type deduction (C++14):

auto add(int a, int b) { return a + b; } // return type: int

Structured bindings (C++17):

std::pair<int, std::string> p{1, "one"};
auto [num, name] = p;   // num: int, name: std::string

decltype — Type Introspection

decltype(expr) yields the type of an expression without evaluating it.

int x = 0;
decltype(x) y = 5;        // y is int
decltype(x + 0.0) z = 1.5; // z is double

std::vector<int> v;
decltype(v)::iterator it = v.begin(); // vector<int>::iterator

decltype(auto) (C++14) — deduces type including references and cv-qualifiers:

int x = 42;
int& ref = x;
decltype(auto) a = ref;  // a is int& (reference preserved)
auto b = ref;            // b is int (reference stripped)

Trailing Return Types

Used when the return type depends on parameter types:

template <typename A, typename B>
auto multiply(A a, B b) -> decltype(a * b) {
    return a * b;
}

auto in Lambdas (C++14 Generic Lambdas)

auto greet = [](auto name) {
    return std::string("Hello, ") + name;
};
greet("world");   // works with any string-like type

Common Pitfalls

auto strips references and cv-qualifiers:

const int& cref = x;
auto a = cref;       // a is int (not const int&!)
auto& b = cref;      // b is const int& (reference preserved)
const auto& c = cref; // c is const int&

[!WARNING] When using auto with references or const-qualified types, always add & or const auto& to preserve those qualifiers.


auto Type Deduction Flow

flowchart TD A[“auto x = expr”] –> B{“Is expr a reference?”} B –>|”Yes”| C[“Strip reference”] C –> D{“Has cv-qualifiers?”} B –>|”No”| D D –>|”Yes”| E[“Strip cv-qualifiers”] D –>|”No”| F[“Deduce base type”] E –> F F –> G[“x has deduced type”]

decltype vs auto Decision

flowchart TD A[“Need to capture a type”] –> B{“Preserve references and cv?”} B –>|”Yes”| C[“Use decltype(auto)”] B –>|”No”| D{“Have initializer expression?”} D –>|”Yes”| E[“Use auto”] D –>|”No”| F{“Have an expression to inspect?”} F –>|”Yes”| G[“Use decltype(expr)”] F –>|”No”| H[“Use explicit type”]


Summary Table

FeatureSyntaxWhat it deducesNotes
auto variableauto x = expr;Type of expr (strips ref/cv)C++11
auto returnauto fn() { ... }Return expression typeC++14
Structured bindingauto [a,b] = pair;Member typesC++17
decltype(expr)decltype(x+1) v;Exact type of expressionNo evaluation
decltype(auto)decltype(auto) x = expr;Type preserving ref and cvC++14
Trailing returnauto f() -> decltype(...)Expression-dependent returnC++11