Modern C++ by example: six idioms you can paste into a project today
Every example below is a complete program. Build with g++ -std=c++23 -Wall -Wextra file.cpp (GCC 14+) or the Clang/MSVC equivalent. Snippets are MIT licensed; copy freely.
1. Structured bindings: unpack pairs, tuples and structs
#include <map>
#include <print>
#include <string>
struct Point { double x, y; };
int main() {
std::map<std::string, int> stock{{"bolts", 40}, {"nuts", 12}};
for (const auto& [name, qty] : stock)
std::println("{:<8} {}", name, qty);
auto [inserted_it, ok] = stock.insert({"washers", 3});
std::println("inserted={} name={}", ok, inserted_it->first);
Point p{3.0, 4.0};
auto [x, y] = p;
std::println("dist={}", std::sqrt(x * x + y * y));
}
2. std::optional with monadic operations (C++23)
#include <charconv>
#include <optional>
#include <print>
#include <string_view>
std::optional<int> to_int(std::string_view s) {
int v{};
auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), v);
if (ec == std::errc{} && ptr == s.data() + s.size()) return v;
return std::nullopt;
}
int main() {
for (std::string_view in : {"42", "x", "-7"}) {
auto msg = to_int(in)
.and_then([](int v) -> std::optional<int> { return v >= 0 ? std::optional{v * 2} : std::nullopt; })
.transform([](int v) { return std::format("doubled: {}", v); })
.value_or("not a non-negative integer");
std::println("{:>3} -> {}", in, msg);
}
}
3. Ranges: lazy pipelines that read top to bottom
#include <algorithm>
#include <print>
#include <ranges>
#include <vector>
struct Order { int id; double total; bool paid; };
int main() {
std::vector<Order> orders{{1, 20.0, true}, {2, 55.5, false}, {3, 99.0, true}, {4, 5.0, true}};
auto big_paid = orders
| std::views::filter([](const Order& o) { return o.paid && o.total > 10; })
| std::views::transform(&Order::id);
for (int id : big_paid) std::print("{} ", id); // 1 3
std::println();
std::ranges::sort(orders, std::ranges::greater{}, &Order::total);
std::println("largest: #{}", orders.front().id); // #3
auto evens = std::views::iota(1) | std::views::filter([](int n) { return n % 2 == 0; }) | std::views::take(5);
std::println("{}", std::ranges::to<std::vector>(evens)); // [2, 4, 6, 8, 10]
}
4. Concepts: constrain templates and get readable errors
#include <concepts>
#include <print>
#include <string>
template <typename T>
concept Shape = requires(const T& s) {
{ s.area() } -> std::convertible_to<double>;
{ s.name() } -> std::convertible_to<std::string>;
};
struct Circle { double r; double area() const { return 3.14159 * r * r; } std::string name() const { return "circle"; } };
struct Square { double s; double area() const { return s * s; } std::string name() const { return "square"; } };
void describe(const Shape auto& s) { std::println("{} with area {:.2f}", s.name(), s.area()); }
int main() {
describe(Circle{1.0});
describe(Square{2.0});
// describe(42); // error: 'int' does not satisfy 'Shape' - one line, not 200
}
5. std::variant + std::visit: a closed set of alternatives
#include <print>
#include <string>
#include <variant>
#include <vector>
struct Text { std::string body; };
struct Image { std::string alt; int width, height; };
struct Divider {};
using Block = std::variant<Text, Image, Divider>;
template <class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
int main() {
std::vector<Block> page{Text{"Hello"}, Image{"A cat", 800, 600}, Divider{}};
for (const Block& b : page)
std::visit(overloaded{
[](const Text& t) { std::println("<p>{}</p>", t.body); },
[](const Image& i) { std::println("<img alt=\"{}\" width={} height={}>", i.alt, i.width, i.height); },
[](Divider) { std::println("<hr>"); },
}, b);
}
Add a fourth alternative and every visit that doesn't handle it fails to compile, which is exactly what you want.
6. A ten-line scope guard
#include <print>
#include <utility>
template <class F>
class scope_exit {
F f_; bool active_{true};
public:
explicit scope_exit(F f) : f_(std::move(f)) {}
~scope_exit() { if (active_) f_(); }
void release() { active_ = false; }
scope_exit(const scope_exit&) = delete; scope_exit& operator=(const scope_exit&) = delete;
};
int main() {
std::println("begin transaction");
scope_exit rollback{[] { std::println("rollback"); }};
// ... work ...
bool ok = true;
if (ok) { rollback.release(); std::println("commit"); }
} // prints "rollback" only if we didn't release
This is the pattern behind std::experimental::scope_exit; until it lands in the standard library, this version is enough.
Comments 0
Log in or register to join the conversation.
No comments yet.