auto and decltype Idioms
`auto` lets the compiler deduce a variable's type from its initialiser; `decltype` extracts the type of an expression for use in declarations. Together they make modern C++ much less verbose, especially for iterators, lambdas, and templates. This snippet covers the basic deduction rules, the difference between `auto` and `auto&`, and when to reach for `decltype` over `auto`.
411 views
8
#include <iostream>
#include <vector>
#include <map>
#include <string>
int main() {
std::vector<int> v = {1, 2, 3, 4};
std::map<std::string, int> m = {{"ada", 36}, {"linus", 54}};
// Without auto: std::vector<int>::iterator it = v.begin();
auto it = v.begin();
std::cout << "first=" << *it << "\n";
// Without auto: std::map<std::string, int>::const_iterator mi = m.cbegin();
auto mi = m.cbegin();
std::cout << mi->first << "=" << mi->second << "\n";
// auto deduces value semantics, dropping const and references.
const int& ref = v[0];
auto a = ref; // a is plain int (copy of ref)
a = 99; // does not affect v[0]
std::cout << "v[0]=" << v[0] << " a=" << a << "\n";
return 0;
}auto deduces the type the way a function template parameter would: it strips top-level const and references by default. So auto a = ref declares a as a fresh int copy, regardless of whether the initialiser is const int& or just int. Use auto to escape the verbose iterator types in the standard library; the alternative for std::map<std::string, int>::const_iterator is unreadable. Keep types explicit at API boundaries (function signatures, public class fields) where the type IS the documentation.
#include <iostream>
#include <vector>
#include <string>
void demoRef() {
std::vector<std::string> words = {"alpha", "beta"};
// auto strips reference: this copies the string.
auto byValue = words[0];
byValue += "!";
std::cout << "copy: " << byValue << " original: " << words[0] << "\n";
// auto& binds a reference; mutation flows back.
auto& byRef = words[1];
byRef += "!";
std::cout << "after mutate words[1]=" << words[1] << "\n";
// const auto& is the right default for read-only views in loops.
for (const auto& w : words) std::cout << w << "\n";
}
int main() {
demoRef();
return 0;
}Add & after auto (and optionally const) to keep reference semantics. auto& is what you want when iterating over a container and need to either mutate or avoid the copy of a non-trivial type. const auto& is the safest default for read-only views because the compiler will catch accidental mutation. The rule of thumb: prefer const auto& in for loops over containers of strings, vectors, maps, or any user-defined type with non-trivial copy.
#include <iostream>
#include <vector>
void demoDecltype() {
std::vector<int> v = {10, 20, 30};
// decltype gives you the EXACT type of an expression, including reference-ness.
decltype(v[0]) ref = v[1]; // int& (operator[] on non-const vector returns int&)
ref = 99;
std::cout << "v[1]=" << v[1] << "\n"; // 99
// decltype(auto) preserves reference-ness during deduction; auto would drop it.
auto plainCopy = v[0]; // int
decltype(auto) realRef = v[0]; // int&
realRef = 7;
std::cout << "plainCopy=" << plainCopy << " v[0]=" << v[0] << "\n";
}
int main() {
demoDecltype();
return 0;
}decltype(expr) yields the type of the expression without evaluating it, preserving cv-qualifiers and reference-ness. That is what you want when writing perfect-forwarding wrappers or trailing return types: auto wrap(T t) -> decltype(t.size()) { return t.size(); }. decltype(auto) (C++14) is the deducer-form that keeps references, useful when forwarding the return value of an inner call without losing aliasing. Reach for decltype only when ordinary auto loses the information you need; in everyday code, auto is enough.
