What Did C++17 Bring? Breakdown of the Main Standard Novelties
\n\nIf you write in C++ and want to stay in the mainstream, then C++17 is the version you should master right now. It was released back in 2017, but many projects still "get stuck" on C++14 or even C++11, afraid to switch to the new standard. And that's a mistake.
\n\nIn this article, we will break down the new features of C++17 with real examples. You will learn how to simplify code, get rid of unnecessary checks, and make programs faster. We won't list all 105 changes — only what is truly useful in practice.
\n\n1. Structured Bindings — Decomposition in a Second
\n\nOne of the most convenient C++17 features is structured bindings. Previously, to unpack a tuple or a pair, you had to write std::tie or access by indices. Now everything is much more elegant.
Example: Returning Multiple Values from a Function
\n\n#include <iostream>\n#include <tuple>\n#include <string>\n\nstd::tuple<int, std::string, double> getData() {\n return {42, "Answer", 3.14};\n}\n\nint main() {\n // C++17: one line instead of std::tie\n auto [id, name, value] = getData();\n std::cout << name << ": " << id << " (" << value << ")\\n";\n return 0;\n}\n\nWhat changed? Previously, you had to declare variables separately and then call std::tie. Now auto [a, b, c] does all the work for you.
Structured bindings work not only with tuples but also with arrays and any structures that have public fields. This is one of the innovations of C++17 that makes code cleaner and more readable.
\n\n2. std::optional — Getting Rid of "Magic" Values
\n\nHow many times have you seen code where a function returns -1 or nullptr to indicate an error? Or worse — a bool flag and a result by reference? std::optional<T> solves this problem once and for all.
Example: Safe Search in a Container
\n\n#include <iostream>\n#include <optional>\n#include <map>\n#include <string>\n\nstd::optional<int> findValue(const std::map<std::string, int>& m, const std::string& key) {\n auto it = m.find(key);\n if (it != m.end()) {\n return it->second; // value exists\n }\n return std::nullopt; // no value\n}\n\nint main() {\n std::map<std::string, int> ages = {{"Alice", 30}, {"Bob", 25}};\n \n auto result = findValue(ages, "Charlie");\n if (result) {\n std::cout << "Found: " << *result << "\\n";\n } else {\n std::cout << "Key not found\\n";\n }\n \n // Or use value_or for a default value\n int age = result.value_or(0);\n return 0;\n}\n\nstd::optional is a container that can either contain a value or be empty. You check it with if (opt), and get the value via *opt or opt.value(). This is much safer than "conventional" error codes.
3. if constexpr — Templates Without Headaches
\n\nC++17 new features for metaprogramming are a real gift. if constexpr allows you to perform conditional compilation inside templates without SFINAE acrobatics.
Example: Universal Function for Different Types
\n\n#include <iostream>\n#include <type_traits>\n#include <string>\n\ntemplate <typename T>\nvoid printInfo(const T& value) {\n if constexpr (std::is_integral_v<T>) {\n std::cout << "Integer: " << value << "\\n";\n } else if constexpr (std::is_floating_point_v<T>) {\n std::cout << "Floating point: