Lesson 23 • Advanced
C++17 & C++20 Modern Features
Master structured bindings, optional, variant, concepts, ranges, string_view, and other modern C++ features that write cleaner, safer code.
What You'll Learn
- ✓ Structured bindings for tuples, pairs, maps
- ✓ optional and variant for safe value handling
- ✓ Concepts for constrained templates (C++20)
- ✓ string_view for zero-copy string operations
The Modern C++ Evolution
C++17 and C++20 introduced features that make C++ feel almost like a different language compared to C++11. These features reduce boilerplate, prevent bugs at compile time, and express intent more clearly.
| Feature | Standard | Replaces |
|---|---|---|
| Structured bindings | C++17 | std::tie, manual .first/.second |
| optional | C++17 | Sentinel values, out-params |
| variant | C++17 | Unsafe unions |
| Concepts | C++20 | SFINAE, enable_if |
| string_view | C++17 | const string& for read-only |
Structured Bindings
Decompose structs, tuples, pairs, and maps elegantly
#include <iostream>
#include <map>
#include <tuple>
#include <vector>
using namespace std;
// Structured bindings (C++17)
struct Point { double x, y, z; };
tuple<string, int, bool> getUserInfo() {
return {"Alice", 28, true};
}
pair<bool, string> validate(int age) {
if (age < 0) return {false, "Age cannot be negative"};
if (age > 150) return {false, "Age seems unrealistic"};
return {true, "Valid"};
}
int main() {
cout << "=== Structured Bindings (C++17) ===" << endl;
...optional & variant
Handle missing values safely and create type-safe unions
#include <iostream>
#include <optional>
#include <variant>
#include <string>
#include <vector>
using namespace std;
// optional — a value that might not exist
optional<int> findIndex(const vector<string>& v, const string& target) {
for (size_t i = 0; i < v.size(); i++) {
if (v[i] == target) return i;
}
return nullopt; // No value
}
optional<double> safeDivide(double a, double b) {
if (b == 0.0) return nullopt;
return a / b;
}
// variant — type-safe union
using Jso
...Concepts, Ranges & string_view
Constrain templates with concepts and process strings without copies
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <string>
using namespace std;
// Concepts (C++20) — constrain template parameters
// Note: Some compilers need -std=c++20
// Simple concept
template <typename T>
concept Numeric = is_arithmetic_v<T>;
template <Numeric T>
T square(T x) {
return x * x;
}
// Concept with requires clause
template <typename T>
concept Printable = requires(T t) {
{ cout << t } -> same_as<ostream&>;
};
template <Printabl
...Common Mistakes
⚠️ Accessing empty optional: Calling .value() on an empty optional throws bad_optional_access. Use .value_or() or check .has_value() first.
⚠️ Dangling string_view: string_view doesn't own the string. If the underlying string is destroyed, the view becomes dangling.
⚠️ variant visit without all types: The visitor passed to visit() must handle every type in the variant, or compilation fails.
Pro Tips
💡 Use optional for "maybe" values: Replace -1, nullptr, and boolean out-params with optional for clearer intent.
💡 Concepts over SFINAE: Concepts give much clearer error messages than enable_if and SFINAE tricks.
💡 string_view for function params: Accept string_view instead of const string& to avoid unnecessary allocations from string literals.
📋 Quick Reference
| Feature | Syntax |
|---|---|
| Structured binding | auto [a, b] = pair; |
| Optional | optional<T> val = nullopt; |
| Variant | variant<int, string> v; |
| Visit | visit(visitor, v); |
| Concept | template <Numeric T> |
| string_view | string_view sv = "text"; |
Lesson Complete!
You now command the most important modern C++ features. Next: Operator Overloading — overloading operators correctly and safely.
Sign up for free to track which lessons you've completed and get learning reminders.