C++17: Standardul practic
Explorați cele mai importante caracteristici ale C++17: legături structurate, std::optional/variant/any, std::string_view, std::filesystem, dacă constexpr și atribute noi.
De ce contează C++17
C++17 este adesea numit „standardul practic”, deoarece a introdus o mulțime de caracteristici care simplifică codarea de zi cu zi fără a necesita cunoștințe profunde de metaprogramare a șablonului. A eliminat bagajele vechi (cum ar fistd::auto_ptrși trigrafele), a adăugat tipuri de vocabular (std::optional,std::variant,std::any) și a introdus zahăr sintactic care face codul mai scurt și mai clar. Dacă scrieți astăzi C++ de producție, C++17 ar trebui să fie linia de bază minimă.
Legături structurate și declarații de inițializare
Legăturile structurate vă permit să despachetați agregate (structuri, matrice, perechi, tupluri) în variabile numite într-o singură declarație. Combinate cu inițializatoareleif/switch, acestea elimină multe variabile temporare:
#include <iostream>
#include <map>
#include <string>
#include <tuple>
std::tuple<std::string, int, double> get_employee() {
return {"Alice", 42, 95000.0};
}
int main() {
// Structured binding with tuple
auto [name, age, salary] = get_employee();
std::cout << name << " is " << age << " years old\n";
// Structured binding with map iteration
std::map<std::string, int> scores{{"Alice", 95}, {"Bob", 87}};
for (const auto& [student, score] : scores) {
std::cout << student << ": " << score << "\n";
}
// if with initializer — the variable is scoped to the if/else
if (auto it = scores.find("Alice"); it != scores.end()) {
std::cout << "Found: " << it->second << "\n";
} else {
std::cout << "Not found\n";
}
// 'it' no longer exists here — no scope leakage
// Structured binding with array
int arr[3] = {10, 20, 30};
auto [x, y, z] = arr;
std::cout << x << ", " << y << ", " << z << "\n";
return 0;
}Tipuri de vocabular: opțional, variantă, orice
std::optionalreprezintă o valoare care poate sau nu să fie prezentă (înlocuind valorile santinelă și parametrii de ieșire).std::varianteste o uniune sigură.std::anydeține orice tip copiabil cu verificarea tipului de rulare:
#include <iostream>
#include <optional>
#include <variant>
#include <any>
#include <string>
// optional: replaces "return -1 on failure" patterns
std::optional<int> find_index(const std::string& haystack, char needle) {
for (size_t i = 0; i < haystack.size(); ++i) {
if (haystack[i] == needle) return static_cast<int>(i);
}
return std::nullopt; // no value
}
int main() {
// std::optional
auto idx = find_index("hello", 'l');
if (idx.has_value()) {
std::cout << "Found at index " << *idx << "\n"; // 2
}
std::cout << idx.value_or(-1) << "\n"; // safe default
// std::variant — type-safe union
std::variant<int, double, std::string> value = "hello";
std::cout << std::get<std::string>(value) << "\n";
// Visit pattern — exhaustive handling of all types
std::visit([](const auto& v) {
std::cout << "Value: " << v << "\n";
}, value);
value = 3.14; // now holds a double
std::cout << std::get<double>(value) << "\n";
// std::any — truly any type, with runtime checking
std::any data = 42;
std::cout << std::any_cast<int>(data) << "\n";
data = std::string("world");
std::cout << std::any_cast<std::string>(data) << "\n";
return 0;
}string_view și sistem de fișiere
std::string_vieweste o referință ușoară, care nu deține un șir. Evită copiile atunci când trebuie să citiți doar un șir.std::filesystemoferă manipulare portabilă, multiplatformă a căilor și operații de fișiere:
#include <iostream>
#include <string>
#include <string_view>
#include <filesystem>
namespace fs = std::filesystem;
// string_view avoids copying — just a pointer + length
void print_trimmed(std::string_view sv) {
auto start = sv.find_first_not_of(' ');
auto end = sv.find_last_not_of(' ');
if (start != std::string_view::npos) {
std::cout << sv.substr(start, end - start + 1) << "\n";
}
}
int main() {
std::string name = " Hello, C++17! ";
print_trimmed(name); // no copy — string_view binds to string
print_trimmed(" world "); // no copy — binds to string literal
// std::filesystem — portable path operations
fs::path source_dir = "/home/user/project/src";
std::cout << "Filename: " << source_dir.filename() << "\n";
std::cout << "Parent: " << source_dir.parent_path() << "\n";
std::cout << "Exists: " << fs::exists(source_dir) << "\n";
// Iterate directory entries
for (const auto& entry : fs::directory_iterator(".")) {
if (entry.is_regular_file()) {
std::cout << entry.path().filename()
<< " (" << entry.file_size() << " bytes)\n";
}
}
// Create directories recursively
fs::create_directories("output/logs/2024");
return 0;
}dacă constexpr, CTAD și Mai mult
if constexprpermite ramificarea în timp de compilare în interiorul șabloanelor - ramurile care nu se potrivesc sunt eliminate în întregime. Class Template Argument Deduction (CTAD) vă permite să omiteți argumentele șablon atunci când compilatorul le poate deduce. Fold expresiile simplifică pachetele de parametri de șablon variadic:
#include <iostream>
#include <string>
#include <vector>
#include <type_traits>
// if constexpr — branches resolved at compile time
template<typename T>
std::string to_string_safe(T value) {
if constexpr (std::is_arithmetic_v<T>) {
return std::to_string(value);
} else if constexpr (std::is_same_v<T, std::string>) {
return value;
} else {
return "[unsupported type]";
}
}
// Fold expressions — collapse parameter packs
template<typename... Args>
auto sum(Args... args) {
return (args + ...); // unary right fold
}
template<typename... Args>
void print_all(Args&&... args) {
((std::cout << args << " "), ...); // fold over comma
std::cout << "\n";
}
int main() {
std::cout << to_string_safe(42) << "\n";
std::cout << to_string_safe(std::string("hi")) << "\n";
std::cout << sum(1, 2, 3, 4, 5) << "\n"; // 15
print_all(1, "hello", 3.14); // 1 hello 3.14
// CTAD — no need for std::vector<int>
std::vector v{1, 2, 3, 4, 5}; // deduced as vector<int>
std::pair p{"hello", 42}; // deduced as pair<const char*, int>
// Nested namespaces
// namespace A::B::C { } instead of namespace A { namespace B { namespace C { } } }
// Inline variables — can be defined in headers without ODR violations
// inline constexpr int version = 17;
return 0;
}C++17 a standardizat trei atribute importante:
[[nodiscard]] — Avertizează dacă o valoare returnată este eliminată. Folosiți-l pe funcții a căror valoare de returnare ar trebui întotdeauna verificată (coduri de eroare, resurse alocate, funcții din fabrică).
[[maybe_unused]] — Suprimă avertismentele privind variabile/parametrii neutilizate. Util pentru variabilele utilizate numai în versiunile de depanare sau codul specific platformei.
[[fallthrough]] — Indică căderea intenționată într-un cazswitch, eliminând avertismentele compilatorului.
Adoptă[[nodiscard]]în mod agresiv pe API-urile tale. Un cod de eroare eliminat este o eroare care așteaptă să se întâmple.
Never return astd::string_viewthat references a local string. Sincestring_viewdoes not own its data, the underlying string may be destroyed before the view is used, causing undefined behavior:
// DANGEROUS — returns a view to a destroyed temporary!
std::string_view bad() {
std::string s = "hello";
return s; // s is destroyed at end of function!
}Rule of thumb: use string_view for parameters (reading data), return std::string for return values (owning data).
- Structured bindings (
auto [a, b] = ...) unpack tuples, pairs, structs, and arrays cleanly std::optionalreplaces sentinel values;std::variantreplaces unsafe unions;std::anyfor truly dynamic typesstd::string_viewevită copiile pentru accesul la șiruri de caractere doar pentru citire, dar ferește-te de referințele agățateif constexprpermite ramificarea în timp de compilare în șabloane, eliminând boilerplate SFINAE- CTAD permite compilatorului să deducă argumente șablon:
std::vector v{1,2,3}funcționează - Utilizați
[[nodiscard]]pentru funcțiile ale căror valori returnate nu trebuie ignorate
Test — Verifică-ți cunoștințele
(15 XP)1. Ce face`auto [x, y] = std::make_pair(1, 2.0);`în C++17?
2. Ce reprezintă`std::optional<int>`?
3. De ce este periculoasă întoarcerea unui`std::string_view`de la o funcție dacă face referire la un șir local?