Cppauto & decltype

auto & decltype

auto and decltype are two type-deduction tools introduced (and expanded) in modern C++. Both let the compiler figure out a type for you instead of writing it out by hand, which becomes especially valuable with long template and iterator type names.
auto — Deduce from the Initializer
auto tells the compiler: "look at the value on the right-hand side and figure out this variable's type from it." It requires an initializer — there is nothing to deduce from otherwise.

CPP
auto count = 10;          // int
auto price = 19.99;       // double
auto name = std::string{"Ada"}; // std::string
auto ptr = &count;        // int*
auto shines when the real type is long and noisy — most famously with STL iterators:

CPP
#include <vector>
#include <map>

int main() {
    std::map<std::string, std::vector<int>> data;

    // Without auto:
    std::map<std::string, std::vector<int>>::iterator it1 = data.begin();

    // With auto — identical behavior, far more readable:
    auto it2 = data.begin();

    return 0;
}
auto in Range-Based for Loops

CPP
#include <iostream>
#include <vector>

int main() {
    std::vector<int> numbers{1, 2, 3, 4, 5};

    for (auto n : numbers) {          // n deduced as int (copy)
        std::cout << n << " ";
    }

    for (const auto& n : numbers) {   // n deduced as const int& (no copy, read-only)
        std::cout << n << " ";
    }

    return 0;
}
auto can hide important type information
Overusing auto can make code harder to review — a reader may not be able to tell at a glance whether a variable is a value, a reference, a pointer, or an expensive-to-copy object. It can also silently deduce a type you did not intend, such as deducing int when you meant double because the initializer had no decimal point. Use auto when the type is obvious from context (like iterators, or immediately to the right of a constructor call) and prefer writing the type explicitly when clarity matters more than brevity.
decltype — Deduce Without Evaluating
decltype(expression) yields the type of an expression without actually evaluating it. Unlike auto, it does not require an initializer and it preserves references and const-ness exactly, which makes it valuable in generic/template code.

CPP
#include <iostream>

int main() {
    int x = 5;
    int& refX = x;

    decltype(x) a = 10;      // int
    decltype(refX) b = x;    // int& — decltype preserves the reference

    std::cout << a << " " << b << std::endl;
    return 0;
}
decltype in a Template Return Type

A classic use case is a function template whose return type depends on the types of its parameters — for example, adding two values of potentially different types.

CPP
#include <iostream>

// Trailing return type: the return type is deduced from a + b,
// which might be int, double, or some other type depending on T and U.
template <typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
    return a + b;
}

int main() {
    std::cout << add(2, 3.5) << std::endl;   // double, 5.5
    std::cout << add(1, 2) << std::endl;     // int, 3
    return 0;
}
  • auto deduces from a value you already have in hand (the initializer)

  • decltype deduces from an expression's declared type, without evaluating it

  • Since C++14, decltype(auto) combines both: deduce like auto but preserve references/const like decltype

Note
Since C++14, function return types can also use plain auto and let the compiler deduce the return type from the return statement, which avoids the need for a trailing decltype in many simpler cases.