auto & decltype
Some C++ types are long and ugly — iterator types, template results, lambda types. auto lets the compiler figure them out for you, and decltype reports the exact type of an expression when you need precision. Used well, they make code shorter and safer; used carelessly, they hide important detail. This lesson teaches you when each one earns its keep.
Learn auto & decltype in our free C++ course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick recall.
Part of the free C++ course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn
💡 Real-World Analogy
Imagine filling out a form. With explicit types you write the full label every time: "this box holds a 32-bit signed integer". With auto you instead say "whatever fits what I'm putting in" — you hand over a value and the form figures out the right box. It's not weaker typing : the box still has exactly one fixed type, you just let the value decide it. decltype is like asking a clerk "what type is that field over there?" — it tells you the precise declared type of something that already exists, without touching it.
1. auto: Deduce the Type
auto asks the compiler to deduce a variable's type from its initializer. It's still strongly, statically typed — once deduced, the type is locked in. The classic payoff is iterators: instead of writing std::vector<int>::iterator , you write auto . Note that an auto variable must be initialized, since there's nothing to deduce from otherwise.
2. auto Copies — Use auto& to Avoid It
Here's the subtle part: plain auto deduces by value , so it makes a copy and strips away references and top-level const . In a range-based for loop that means for (auto n : v) modifies copies, not the real elements. Use auto& to bind a reference (to modify the originals) or const auto& to read efficiently without copying.
Your turn. The loop below should double each price in place . Fill in the blank with the right form of auto :
🧩 Reorder Challenge
These lines declare a value with auto , derive a decltype type from it, and print. Put them in order:
Open main (D), let price deduce to double (B), declare tax with the same type via decltype(price) (A), print the sum 5.25 (C), then return 0; (E) and close (F). decltype(price) needs price to exist first.
3. decltype: the Exact Type of an Expression
Where auto deduces from a value and strips references and const, decltype(expr) reports the exact declared type of an expression — including const and references — and it does not evaluate the expression. This makes it the precise tool when you must mirror the type of something that already exists, such as the result type of a + b in a template.
4. Deduced Return Types
Since C++14, a function can use auto as its return type and the compiler deduces it from the return statements — invaluable for templates where the result type depends on the inputs. The older trailing return type syntax, auto f(...) -> decltype(...) , spells the type out explicitly using decltype , which is occasionally clearer or required.
🔎 Deep Dive: auto's deduction rules & decltype(auto)
Plain auto follows the same rules as template argument deduction: it deduces by value , dropping top-level const and references. You opt back in explicitly:
decltype(auto) is a special return-type form that uses decltype's rules instead of auto's, so it preserves references and const. It's mainly for generic wrappers that must forward an expression's exact type:
Pro Tips
- 💡 Use const auto& in loops over big objects: it reads them without copying.
- 💡 Reach for auto on iterators & lambdas: these types are long or unnameable.
- 💡 Spell the type out when it aids the reader: auto that hides a surprising type hurts clarity.
- 💡 Remember auto needs an initializer: auto x; won't compile — there's nothing to deduce from.
🧠 Quick Recall
Predict the output before revealing the answer.
1 — plain auto copies each element, so n += 100 changes copies, not the vector. Use auto& to actually modify it.
3 — 7 / 2 is integer division giving an int 3, so x is deduced as int . auto doesn't change the math.
99 — decltype(a) is int , so b is an int& bound to a . Assigning to b updates a .
Common Errors (and the fix)
- Uninitialized auto : auto x; fails — there's no value to deduce from. Give it an initializer.
- Expecting a loop to mutate with plain auto : for (auto e : v) copies. Use auto& to change elements.
- Unintended copies of big objects: for (auto s : strings) copies every string. Prefer const auto& .
- Confusing decltype(x) with decltype((x)) : the extra parentheses make it a reference type ( int& instead of int ). The parens matter.
- Over-using auto : when the deduced type surprises a reader, write it out — clarity beats brevity.
📋 Quick Reference
Form
Deduces
auto x = v;
by value, drops const & ref
auto& x = v;
reference to v
const auto& x = v;
read-only ref, no copy
decltype(expr)
exact type, keeps const & ref
auto f() { ... }
return type from return stmt
auto f() -> decltype(e)
trailing return type
Frequently Asked Questions
Mini-Challenge: Iterate a Map with auto
Put const auto& and structured bindings to work iterating a map, and let auto hold your running total.
🎉 Lesson Complete
- ✅ auto deduces a variable's type from its initializer (and must be initialized)
- ✅ Plain auto copies — use auto& to modify, const auto& to read big objects
- ✅ decltype(expr) gives the exact type, keeping const and references
- ✅ Functions can deduce return types via auto or trailing -> decltype(...)
- ✅ decltype(auto) forwards an expression's exact type in generic code
- ✅ Next lesson: Range-Based for Loops — iterate without indices
Practice quiz
What does 'auto' do in 'auto x = 5;'?
- Makes x dynamically typed
- Makes x a reference
- Deduces x's type (int) from the initializer at compile time
- Defers x's type until runtime
Answer: Deduces x's type (int) from the initializer at compile time. auto deduces the type from the initializer; the type is still fixed and checked at compile time.
In a range loop, 'for (auto n : v) n *= 10;' fails to change the vector because:
- Plain auto deduces by value, so n is a copy
- auto is read-only
- The loop never runs
- 10 is too large
Answer: Plain auto deduces by value, so n is a copy. Plain auto copies each element; use 'auto&' to bind a reference and modify the originals.
Which form modifies the container's elements AND avoids copying when only reading is needed?
- auto& for both
- const auto for both
- decltype(auto) only
- auto for writing; const auto& for reading
Answer: auto for writing; const auto& for reading. Use auto& to modify originals, and const auto& to read big objects without copying.
What does plain 'auto' drop during deduction?
- Nothing
- Top-level const and references
- Only references
- Only const
Answer: Top-level const and references. Plain auto deduces by value, stripping top-level const and references; opt back in with auto&, const auto&, etc.
How does decltype(expr) differ from auto?
- It reports the exact declared type, keeping const and references
- It evaluates the expression
- It only works on integers
- It is the same as auto
Answer: It reports the exact declared type, keeping const and references. decltype gives the precise declared type (including const and &) without evaluating the expression.
What is the type of x in 'auto x = 7 / 2;' and what does it print?
- double, 3.5
- int, 4
- int, 3
- float, 3.5
Answer: int, 3. 7 / 2 is integer division giving int 3; auto deduces int and doesn't change the math.
Given 'int a = 5; decltype((a)) c = a;', what is c's type?
- int (a copy)
- int& (a reference to a)
- const int
- double
Answer: int& (a reference to a). The extra parentheses make decltype((a)) yield int& — a reference — so assigning to c changes a.
What does the trailing-return syntax 'auto add(A a, B b) -> decltype(a + b)' do?
- Always returns int
- Returns void
- Forbids deduction
- Spells out the return type explicitly using decltype of the expression
Answer: Spells out the return type explicitly using decltype of the expression. It declares the return type after the parameters using decltype, useful when the result type depends on the arguments.
Why does 'auto x;' fail to compile?
- auto is reserved
- There is no initializer for the compiler to deduce the type from
- x must be uppercase
- auto needs a semicolon
Answer: There is no initializer for the compiler to deduce the type from. An auto variable must be initialized, since the type is deduced from the initializer.
What is decltype(auto) mainly used for?
- Looping faster
- Declaring globals
- Forwarding an expression's exact type (preserving references/const) in generic code
- Disabling const
Answer: Forwarding an expression's exact type (preserving references/const) in generic code. decltype(auto) uses decltype's rules so a generic return can preserve the exact type and value category of an expression.
Continue this course
- Previous: Lambda Expressions
- Next: Range-Based for Loops