C++17
A large, practical release. Structured bindings clean up loop bodies. if constexpr replaces SFINAE in function templates. std::variant, std::optional, and std::any bring sum types and nullable values to the standard library. std::filesystem ends platform-specific path hell.
Core Language
Unpack tuples, pairs, structs, and arrays into named variables. Cleaner loop bodies, cleaner returns.
#include <map>
#include <tuple>
// Unpack pair from map iteration
std::map<std::string, int> scores{{"Alice", 95}, {"Bob", 87}};
for (const auto& [name, score] : scores) {
std::cout << name << ": " << score << '\n';
}
// Unpack tuple
auto [x, y, z] = std::make_tuple(1, 2.0, "three");
// Unpack struct
struct Point { int x, y; };
Point p{3, 4};
auto [px, py] = p;Compile-time if that discards the not-taken branch entirely. Replaces enable_if and SFINAE for template specialization in function bodies.
template<typename T>
auto process(T val) {
if constexpr (std::is_integral_v<T>) {
return val * 2; // only compiled for integral types
} else if constexpr (std::is_floating_point_v<T>) {
return val * 2.0; // only compiled for floating-point types
} else {
static_assert(false, "unsupported type");
}
}
process(5); // returns int 10
process(2.5); // returns double 5.0Deduce class template arguments from constructor parameters. No more make_pair, make_tuple, make_unique boilerplate.
#include <vector>
#include <pair>
#include <tuple>
// C++14 — had to specify or use make_*
std::pair<int, std::string> p14{1, "hello"};
// C++17 — deduced from constructor
std::pair p17{1, "hello"}; // pair<int, const char*>
std::vector v{1, 2, 3, 4, 5}; // vector<int>
std::tuple t{1, 2.0, "three"}; // tuple<int, double, const char*>Apply a binary operator over a variadic parameter pack. Replaces recursive template instantiation for variadic operations.
template<typename... Args>
auto sum(Args... args) {
return (args + ...); // left fold: ((a + b) + c) + ...
}
template<typename... Args>
bool all_positive(Args... args) {
return ((args > 0) && ...); // fold with &&
}
sum(1, 2, 3, 4, 5); // 15
all_positive(1, 2, -3); // falseVariables in headers without multiple-definition errors. Essential for constexpr inline constants and header-only libraries.
// constants.h — safe to include in multiple TUs
inline constexpr double PI = 3.141592653589793;
inline constexpr int MAX_RETRIES = 3;
// C++14 required workarounds like:
// static constexpr double PI = 3.14; // or extern + .cpp definitionLibrary
Type-safe union. Knows which alternative is active. std::visit provides exhaustive pattern matching.
#include <variant>
using Shape = std::variant<Circle, Rectangle, Triangle>;
double area(const Shape& s) {
return std::visit([](const auto& shape) {
return shape.area();
}, s);
}
Shape s = Circle{5.0};
double a = area(s); // 78.54...
// holds_alternative, get, get_if
if (std::holds_alternative<Circle>(s)) {
auto& c = std::get<Circle>(s);
}An optional value — either T or nothing. Replaces null pointers, sentinel values, and bool+output-param patterns.
#include <optional>
std::optional<int> find_index(
const std::vector<int>& v, int target)
{
auto it = std::ranges::find(v, target);
if (it == v.end()) return std::nullopt;
return std::distance(v.begin(), it);
}
auto idx = find_index(v, 42);
if (idx) {
std::cout << "found at " << *idx << '\n';
}
int pos = idx.value_or(-1);Cross-platform file system operations: paths, directory iteration, file info, copy, rename, create_directories.
#include <filesystem>
namespace fs = std::filesystem;
// Path operations
fs::path p = "/home/user/file.txt";
p.filename(); // "file.txt"
p.stem(); // "file"
p.extension(); // ".txt"
p.parent_path(); // "/home/user"
// Directory iteration
for (const auto& entry : fs::directory_iterator(".")) {
if (entry.is_regular_file()) {
std::cout << entry.path().filename()
<< " " << entry.file_size() << " bytes\n";
}
}
fs::create_directories("a/b/c");
fs::copy("src.txt", "dst.txt");Non-owning, read-only view over a character sequence. Pass string data without copying, without owning, without null-terminator requirement.
#include <string_view>
// Takes any string-like without allocation
void log(std::string_view msg) {
std::cout << msg; // works with string, literal, span<char>
}
log("hello"); // literal — no allocation
log(std::string{"world"}); // string — no copy
log(std::string_view{buf, buf_len}); // raw buffer — no copy
// Efficient substr — returns another view, no allocation
std::string_view sv = "hello world";
auto hello = sv.substr(0, 5); // "hello" — no copystd::execution_policy controls parallel execution of STL algorithms. std::par, std::par_unseq for CPU parallelism.
#include <algorithm>
#include <execution>
#include <vector>
std::vector<int> v(1'000'000);
std::iota(v.begin(), v.end(), 0);
// Parallel sort — uses all CPU cores
std::sort(std::execution::par, v.begin(), v.end());
// Parallel + vectorized transform
std::transform(std::execution::par_unseq,
v.begin(), v.end(), v.begin(),
[](int x) { return x * 2; });