Templates and Modern C++
6 pages · ~4 min✓ Reviewed
Builds on Strings and string_view. Next up: Concurrency and Threads.
Function and Class Templates
1. Blueprint and Deduction
- Template = compile-time blueprint
- Instantiated per distinct
T: each type gets its own generated code max(1, 2)deducesT= intmax(1, 2.5)fails:Tconflicts, so giveTexplicitly
cpp
template<typename T>
T max(T a, T b) { return a > b ? a : b; }
max(1, 2); // T = int
max<double>(1, 2.5); // explicit T2. Class Templates and Specialization
| Before C++17 | C++17 | |
|---|---|---|
| Class args | explicit: std::vector<int> | CTAD deduces: std::pair p{1, 'a'} |
- Full specialization
template<> struct S<int>{}replaces one type - Function templates: no partial specialization
- Use overloads or
if constexprinstead
3. Common Mistakes
- Definitions not in the header: linker reports undefined reference
- Dependent name without
typename: writetypename T::value_type
auto, decltype and Range-for
1. auto deduction
auto x = exprdeduces from the initializer- Drops top-level const and references:
const int& rgivesint
| Form | Result | Use |
|---|---|---|
auto& | keeps ref and const | modify in place |
const auto& | binds to anything | no copy |
auto&& | forwarding reference | lvalue or rvalue |
2. decltype and decltype(auto)
decltype(expr)gives the exact declared type, const and refs keptdecltype((x))with double parentheses givesT&decltype(auto)(C++14) returns with decltype rules
cpp
int x = 0; decltype(x) a = x; // int decltype((x)) b = x; // int& decltype(auto) f() { int y = 1; return (y); // int&, dangling }
3. Range-for
Structured Bindings and constexpr
1. Structured Bindings (C++17)
- Unpack pairs, tuples, arrays, aggregates
auto [a, b]makes a copy of the objectauto& [k, v]binds by reference, so it edits the source
cpp
auto [it, ok] = m.insert({k, v});
for (auto& [key, val] : m) val++;2. constexpr Family
constexprvariable = compile-time constant- Usable as array size or template argument:
constexpr int N = 4; int a[N];
| Keyword | Guarantee |
|---|---|
constexpr function | compile time or run time |
consteval (C++20) | compile time only |
constinit (C++20) | static initialization, still mutable |
3. Mistakes
- Lambdas cannot capture binding names before C++20
constexpris notconst: const may be initialized at run time
optional and variant
1. optional: a T or nothing
optional<T>(C++17): T or nothingif (o) use(*o);ando.value_or(0)for a defaulto.value()throwsbad_optional_accessif empty*oon empty: undefined behaviour
2. variant: exactly one alternative
get<int>(v)throwsbad_variant_accesson wrong typeget_if<int>(&v)returns nullptr insteadvisitmust cover every alternative, else compile error
cpp
variant<int, string> v = 3; if (auto* p = get_if<int>(&v)) use(*p); visit(overloaded{ [](int i) { /* ... */ }, [](string s) { /* ... */ } }, v);
visit dispatches on the active type
3. Which one to use
| Situation | Use |
|---|---|
| Value may be absent | optional<T> |
| Closed set of types | variant<...> |
| Neither | sentinel or pointer |
Concepts and C++ Standards Cheatsheet
1. Concepts (C++20)
- A concept names a compile-time constraint on a type
- Use it as
template<Number T>,Number auto x, orrequires Number<T>
cpp
template<typename T> concept Number = std::is_arithmetic_v<T>; template<Number T> T twice(T x); void f(Number auto x); template<typename T> requires Number<T> void g(T x);
three ways to apply Number
- A failed constraint gives a readable error, not pages of template noise
- A requires expression checks syntax:
requires(T a) { a + a; { a.size() } -> std::convertible_to<size_t>; }
2. What Each Standard Added
| Std | Added |
|---|---|
| C++11 | auto, decltype, range-for, lambdas, move semantics, constexpr, nullptr, smart pointers |
| C++14 | generic lambdas, decltype(auto), return-type deduction |
| C++17 | structured bindings, CTAD, if constexpr, optional, variant, string_view |
| C++20 |
Check yourself
With std::map<string,int> m, does for (auto [k, v] : m) v++; change the map?
- No: plain
autocopies each pair, sovis a copy. - Use
auto& [k, v]to modify the map.
What is wrong with decltype(auto) f() { int x = 1; return (x); }?
(x)in parentheses makes decltype giveint&.- It returns a reference to a local, which dangles.
Why does max(1, 2.5) fail for template<typename T> T max(T a, T b)?
- T deduces as
intfrom one argument anddoublefrom the other, so deduction conflicts. - Write
max<double>(1, 2.5).