Какие новые возможности появились в C++17? Обзор ключевых изменений и примеры
// Старый способ: возврат пары для результата и флага ошибки
std::pair<int, bool> divide(int a, int b) {
if (b == 0) return {0, false};
return {a / b, true};
}
// Использование
auto result = divide(10, 3);
if (result.second) {
std::cout << result.first;
}
Как это будет выглядеть в C++17?
<<1. Структурные привязки (Structured Bindings) — декомпозиция без боли
Да, вы всё правильно поняли. В C++17 появилась возможность "разворачивать" кортежи, пары, массивы и даже структуры в отдельные переменные одной строкой. Ваш пример с делением переписывается так:
#include <iostream>
#include <optional>
std::optional<int> divide(int a, int b) {
if (b == 0) return std::nullopt; // нет значения
return a / b; // есть значение
}
int main() {
auto result = divide(10, 3);
if (result) { // проверка на наличие значения
std::cout << *result; // разыменование
}
// А если нужна пара — структурные привязки:
std::pair<int, std::string> my_pair = {42, "hello"};
auto [num, str] = my_pair;
std::cout << num << " " << str;
}
Теперь std::optional<T> — это контейнер, который либо содержит значение типа T, либо пуст. Это гораздо читаемее, чем пара (значение, bool). Метод has_value() или просто operator bool проверяет наличие, а value() или * (оператор разыменования) извлекают значение.
2. if constexpr — условная компиляция на этапе компиляции
Раньше для шаблонов приходилось использовать SFINAE или специализации. Теперь можно писать обычный if, который вычисляется во время компиляции:
template <typename T>
auto get_value(T t) {
if constexpr (std::is_integral_v<T>) {
return t + 1;
} else {
return t + 0.5;
}
}
// Если T = int, компилятор сгенерирует только первую ветку
// Если T = double — только вторую
3. Гарантированное копирование (Guaranteed Copy Elision)
В C++17 компилятор обязан пропускать копирование при возврате временных объектов. Это значит, что вы можете смело возвращать из функции большие объекты без боязни лишних копий. Раньше это была оптимизация (NRVO), теперь — гарантия:
std::vector<int> create_vector() {
return std::vector<int>(1000000); // В C++17 — 0 копий!
}
std::vector<int> v = create_vector(); // прямой конструктор, без копирования
4. std::variant — безопасная замена union
Если вам нужно хранить значение одного из нескольких типов, используйте std::variant:
#include <variant>
std::variant<int, double, std::string> v;
v = 42; // хранит int
v = 3.14; // теперь хранит double
v = "hello"s; // теперь строку
// Посещение (visitor) — как switch по типам:
std::visit([](auto&& arg) {
std::cout << arg;
}, v);
5. Что реально использовать в продакшене?
- Обязательно:
std::optional,std::variant,std::any(для динамической типизации), структурные привязки,if constexpr,std::string_view(невладеющее представление строки — избегает копирования). - Рекомендуется:
std::filesystem(работа с файловой системой),std::parallel_algorithms(распараллеливание алгоритмов STL). - С осторожностью:
std::byte(для низкоуровневой работы),inlineпеременные (если не уверены в ODR).
Ваш старый код с парой теперь выглядит так:
std::optional<int> divide(int a, int b) {
if (b == 0) return {};
return a / b;
}
int main() {
if (auto result = divide(10, 3); result) {
std::cout << *result; // 3
} else {
std::cout << "Ошибка";
}
}
Обратите внимание на if (auto result = ...; result) — это тоже фича C++17: инициализация внутри if.
Дополнение: Новые алгоритмы и параллельное выполнение
В C++17 появилась поддержка параллельного выполнения стандартных алгоритмов. Теперь вы можете добавить политику выполнения (execution policy) к любому алгоритму из <algorithm>:
#include <algorithm>
#include <execution>
#include <vector>
std::vector<int> data = {3, 1, 4, 1, 5, 9, 2, 6};
// Последовательная сортировка (как раньше)
std::sort(std::execution::seq, data.begin(), data.end());
// Параллельная сортировка (использует несколько потоков)
std::sort(std::execution::par, data.begin(), data.end());
// Векторизованная сортировка (может использовать SIMD-инструкции)
std::sort(std::execution::unseq, data.begin(), data.end());
// Параллельная + векторизованная
std::sort(std::execution::par_unseq, data.begin(), data.end());
Это позволяет с минимальными изменениями кода ускорить обработку больших массивов данных. Однако будьте осторожны: параллельные версии требуют, чтобы операции не имели побочных эффектов (не меняли глобальное состояние).
Также стоит упомянуть std::filesystem — полноценную библиотеку для работы с файловой системой:
#include <filesystem>
namespace fs = std::filesystem;
fs::path p = "/home/user/docs";
if (fs::exists(p)) {
for (auto& entry : fs::directory_iterator(p)) {
std::cout << entry.path() << '\n';
}
}
Это намного удобнее, чем использовать POSIX-функции или boost::filesystem.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги