C++17: optional, variant, structured bindings
C++17 — optional, variant, structured bindings
1. std::optional<T>
"A T, or nothing" — a type-safe alternative to nullable values.
#include <iostream>
#include <optional>
std::optional<int> parse(const std::string& s) {
try { return std::stoi(s); }
catch (...) { return std::nullopt; }
}
int main() {
if (auto n = parse("42"); n)
std::cout << "got " << *n << "\n";
else
std::cout << "failed\n";
std::cout << parse("bad").value_or(-1) << "\n";
}
2. std::variant<...>
A type-safe union: holds exactly one of several types.
#include <iostream>
#include <variant>
#include <string>
int main() {
std::variant<int, std::string, double> v;
v = 42;
std::cout << std::get<int>(v) << "\n";
v = std::string{"hello"};
std::cout << std::get<std::string>(v) << "\n";
std::visit([](const auto& val) {
std::cout << "visiting: " << val << "\n";
}, v);
}
3. Structured Bindings
#include <map>
#include <iostream>
int main() {
std::map<std::string, int> m = {{"a",1},{"b",2}};
for (const auto& [k, v] : m)
std::cout << k << "=" << v << "\n";
// Works with structs too:
struct Point { int x, y; };
Point p{3, 4};
auto [x, y] = p;
}
4. if constexpr
Compile-time if for templates — generates only the matching branch:
template<typename T>
void print(const T& v) {
if constexpr (std::is_pointer_v<T>)
std::cout << *v;
else
std::cout << v;
}
5. Other C++17 Highlights
std::filesystem— paths and FS opsstd::string_view— non-owning string- Mandatory copy elision
[[nodiscard]],[[maybe_unused]]attributes- Class template argument deduction (CTAD)
6. Practice Problems
- A function returning
optional<double>for safe sqrt.✨ Show Answer
std::optional<double> safe_sqrt(double x) { if (x < 0) return std::nullopt; return std::sqrt(x); } - Check if optional has value.
✨ Show Answer
if (opt) { use(*opt); } if (opt.has_value()) { ... } - Variant of int, string, vector.
✨ Show Answer
std::variant<int, std::string, std::vector<int>> v; - Get current type of variant.
✨ Show Answer
v.index(); // 0 = first type, 1 = second, ... - Use std::visit with a generic lambda.
✨ Show Answer
std::visit([](const auto& x){ std::cout << x; }, v); - Structured-bind a std::pair.
✨ Show Answer
auto [first, second] = std::make_pair(1, 2); - When is
if constexpruseful?✨ Show Answer
In templates where some branches only compile for some T. Avoids SFINAE complexity.
- [[nodiscard]] on a function — what does it do?
✨ Show Answer
Compiler warns if the return value is ignored. Useful for "you must check this!" results.
- CTAD example.
✨ Show Answer
std::vector v = {1, 2, 3}; // deduces vector<int> - Why optional over pointer?
✨ Show Answer
Value type — no allocation, no nullability bugs by accident, clearer intent: "maybe-a-value".
- Use std::filesystem to check file existence.
✨ Show Answer
#include <filesystem> if (std::filesystem::exists("data.txt")) { ... } - Set optional to empty.
✨ Show Answer
opt.reset(); // or opt = std::nullopt; - Catch missing variant alternative.
✨ Show Answer
std::get<Bad>(v)throwsstd::bad_variant_access. Or usestd::get_if<T>(&v)which returns nullptr. - Why prefer optional over -1 sentinel?
✨ Show Answer
Type-safe, self-documenting, can't accidentally use -1 as a real value, works for any type (not just numerics).
Summary
C++17 added optional, variant, structured bindings,
and if constexpr. Together they replace many ugly idioms (sentinels, unions, multi-returns).
Plus filesystem, CTAD, and stricter copy elision. Adopt these aggressively in new code.