Modern C++ Hello World
In pre-C++11 code, types had to be spelled out explicitly, strings were passed as const char* or heavy std::string copies, and formatted output required printf (unsafe) or iostream manipulators (verbose).
Modern C++ introduces auto (C++11), string_view (C++17), structured bindings (C++17), and std::format (C++20) to make everyday code shorter, safer, and more readable. Use these as your default vocabulary.
1. auto — let the compiler deduce the type
Reduces verbosity and prevents narrowing bugs from writing the wrong type. Use auto when the type is obvious from the right-hand side of the assignment. Use "const auto&" when you need them preserved.
Watch out: auto strips top-level const and references.
2. string_view — a lightweight, non-owning view of a string
Unlike const std::string&, string_view does not allocate and can bind to string literals, std::string, or substrings. Never return a string_view to a local std::string.
Watch out: the viewed string must outlive the string_view.
auto greet(std::string_view name) -> std::string {
return std::format("Hello, {}! Welcome to Modern C++.", name);
}3. Trailing return type (auto -> Type)
Useful when the return type depends on parameters or when you prefer the function name to appear first for readability. Required for some template and decltype(auto) patterns.
auto add(int a, int b) -> int {
return a + b;
}4. Structured bindings (C++17) — unpack aggregates
Works with pairs, tuples, arrays, and structs with all-public members. Avoids .first/.second boilerplate. Use "auto& [x, y] = ..." to bind by reference.
Watch out: structured bindings create copies by default.
Key Takeaways
- •Prefer auto when the type is clear from context; spell it out when not.
- •Use string_view for read-only string parameters — zero copies, zero allocations.
- •Use std::format instead of iostream manipulators or printf for type-safe output.
- •Structured bindings replace tedious .first/.second access on pairs and tuples.
int main() {
// auto deduces std::string from greet()'s return type
auto message = greet("World");
std::cout << message << '\n';
// Trailing return type — same as writing "int add(int, int)"
std::cout << std::format("add(3, 4) = {}\n", add(3, 4));
// Structured bindings: unpack a pair without .first/.second
auto [x, y] = std::pair{10, 20};
std::cout << std::format("x = {}, y = {}\n", x, y);
// Structured bindings with an array
int arr[] = {100, 200, 300};
auto [a, b, c] = arr;
std::cout << std::format("a = {}, b = {}, c = {}\n", a, b, c);
// const auto& binding — avoids copying
const auto& [cx, cy] = std::pair{42, 99};
std::cout << std::format("cx = {}, cy = {}\n", cx, cy);
return 0;
}