C++20: Cei patru mari și mai mult
Descoperiți caracteristicile transformatoare ale C++20: concepte, intervale, operatorul navei spațiale, formatul std::, inițializatoarele desemnate, std::span și multe altele.
C++20: Un salt generațional
C++20 este cea mai semnificativă actualizare a C++ de la C++11. Introduce patru caracteristici majore — Concepte, Range, Coroutine și Module — fiecare dintre ele schimbă fundamental modul în care este scris codul C++. Dincolo de „Big Four”, C++20 aduce și operatorul navei spațiale,std::format,std::span, inițializatoare desemnate și multe îmbunătățiri constexpr. Această lecție acoperă totul, cu excepția Coroutinelor și Modulelor, care au lecții dedicate.
Concepte: Șabloane de constrângere
Conceptele înlocuiesc SFINAE cu constrângeri lizibile, componabile asupra parametrilor șablonului. Ele produc mesaje de eroare clare atunci când constrângerile nu sunt îndeplinite:
#include <iostream>
#include <concepts>
#include <string>
#include <vector>
#include <numeric>
// Define a custom concept
template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;
// Use concept in a requires clause
template<typename T>
requires Numeric<T>
T add(T a, T b) {
return a + b;
}
// Shorthand syntax — concept as type constraint
auto multiply(Numeric auto a, Numeric auto b) {
return a * b;
}
// Concept with multiple requirements
template<typename Container>
concept Summable = requires(Container c) {
{ c.begin() } -> std::input_or_output_iterator;
{ c.end() } -> std::input_or_output_iterator;
{ c.size() } -> std::convertible_to<std::size_t>;
};
template<Summable Container>
auto sum_container(const Container& c) {
using T = typename Container::value_type;
return std::accumulate(c.begin(), c.end(), T{});
}
int main() {
std::cout << add(3, 4) << "\n"; // 7
std::cout << multiply(2.5, 4.0) << "\n"; // 10.0
std::vector<int> v{1, 2, 3, 4, 5};
std::cout << sum_container(v) << "\n"; // 15
// add(std::string("a"), std::string("b")); // Compile error!
// Error message: constraints not satisfied — Numeric<std::string> is false
return 0;
}Intervale: algoritmi componibile
Gamele aduc STL o abordare funcțională, în stil pipeline. Vizualizările sunt leneșe — nu alocă și nu copiază date, compunând transformări care se execută numai atunci când sunt iterate:
#include <iostream>
#include <ranges>
#include <vector>
#include <string>
#include <algorithm>
int main() {
std::vector<int> numbers{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// Pipeline: filter even, square them, take first 3
auto result = numbers
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * n; })
| std::views::take(3);
for (int n : result) {
std::cout << n << " "; // 4 16 36
}
std::cout << "\n";
// Range-based algorithms with projections
struct Employee {
std::string name;
int salary;
};
std::vector<Employee> team{
{"Alice", 90000}, {"Bob", 75000}, {"Carol", 120000}
};
// Sort by salary using a projection — no custom comparator needed
std::ranges::sort(team, {}, &Employee::salary);
for (const auto& e : team) {
std::cout << e.name << ": " << e.salary << "\n";
}
// iota generates an infinite sequence
for (int i : std::views::iota(1) | std::views::take(5)) {
std::cout << i << " "; // 1 2 3 4 5
}
std::cout << "\n";
return 0;
}Comparație în trei căi și inițializatori desemnați
Operatorul de navă spațială<=>generează toți cei șase operatori de comparație dintr-o singură declarație. Inițializatoarele desemnate aduc inițializarea câmpului numit de la C la C++ cu siguranță suplimentară:
#include <iostream>
#include <compare>
#include <string>
struct Version {
int major;
int minor;
int patch;
// One operator generates ==, !=, <, >, <=, >=
auto operator<=>(const Version&) const = default;
};
struct Config {
std::string host = "localhost";
int port = 8080;
bool ssl = false;
int max_connections = 100;
};
int main() {
Version v1{2, 0, 0};
Version v2{1, 9, 5};
if (v1 > v2) std::cout << "v1 is newer\n"; // true
if (v1 != v2) std::cout << "versions differ\n";
// Strong ordering gives you all 6 operators for free
auto cmp = v1 <=> v2;
if (cmp > 0) std::cout << "v1 > v2\n";
// Designated initializers — name the fields you're setting
Config cfg{
.host = "example.com",
.port = 443,
.ssl = true
// .max_connections keeps its default value (100)
};
std::cout << cfg.host << ":" << cfg.port
<< " SSL=" << std::boolalpha << cfg.ssl
<< " max=" << cfg.max_connections << "\n";
return 0;
}std::format și std::span
std::formataduce formatarea șirurilor asemănătoare Python-ului la C++.std::spaneste o vedere neproprietă asupra unei secvențe adiacente de elemente, înlocuind perechile brute pointer+mărime:
#include <iostream>
#include <format>
#include <span>
#include <vector>
#include <array>
// std::span — works with any contiguous container
void print_values(std::span<const int> data) {
for (int v : data) {
std::cout << std::format("{:>5}", v);
}
std::cout << "\n";
}
int main() {
// std::format — type-safe, positional formatting
std::string msg = std::format("Hello, {}! You are {} years old.", "Alice", 30);
std::cout << msg << "\n";
// Format specifiers
std::cout << std::format("Hex: {:#x}, Oct: {:#o}, Bin: {:#b}\n", 255, 255, 255);
std::cout << std::format("Pi: {:.4f}\n", 3.14159265);
std::cout << std::format("{:<15} {:>10}\n", "Name", "Score");
std::cout << std::format("{:<15} {:>10}\n", "Alice", 95);
// std::span — unifies arrays, vectors, and raw buffers
int c_array[] = {1, 2, 3, 4, 5};
std::vector<int> vec{6, 7, 8, 9, 10};
std::array<int, 3> arr{11, 12, 13};
print_values(c_array); // works with C array
print_values(vec); // works with vector
print_values(arr); // works with std::array
// Subspan — slice without copying
std::span<int> full(vec);
auto first_three = full.subspan(0, 3);
print_values(first_three); // 6 7 8
return 0;
}constexpr Îmbunătățiri și crono
C++20 extinde masiv ceea ce poate ficonstexpr: funcțiile virtuale,dynamic_cast,try-catchși chiarstd::vectorșistd::stringpot fi acum utilizate în contexte constexpr. Aceasta înseamnă mai multe mișcări de calcul la timpul de compilare, rezultând un cod de rulare mai rapid.
Bibliotecacâștigă un sistem complet de calendar și fus orar. Puteți reprezenta date cum ar fi2024y/January/15, puteți face aritmetica calendaristică și puteți face conversii între fusurile orare - toate în siguranță și fără biblioteci externe.
Atribute:[[likely]]și[[unlikely]]indică compilatorului care ramuri sunt căi fierbinți, permițând o generare mai bună a codului în secțiunile critice pentru performanță.
Nu trebuie să utilizați toate C++20 simultan. Începeți cu funcțiile care oferă cea mai mare creștere a productivității cu cea mai mică întrerupere:
1. Operator de navă spațială — adăugați imediatauto operator<=>(const T&) const = default;la tipurile dvs
2. Concepte — utilizați concepte standard (std::integral,std::ranges::range) înainte de a scrie concepte personalizate
3. std::format — înlocuiți formatareaprintfșistringstream
4. Intervalele — începe custd::views::filterșistd::views::transformîn codul nou
5. Inițializatoare desemnate — utilizați pentru structurile de configurare și tipurile de opțiuni
Salvați Coroutine și Module pentru când echipa și lanțul dvs. de instrumente sunt gata.
- Conceptele înlocuiesc SFINAE cu constrângeri de șablon care pot fi citite și compuse și cu mesaje de eroare clare
- Gamele oferă conducte leneșe, componabile folosind operatorul
|- nu sunt necesare containere temporare - Operatorul de navă spațială
<=>generează toți cei șase operatori de comparație dintr-o declarație implicită std::formataduce la C++ formatarea șirurilor de caractere sigure, în stil Pythonstd::spaneste o vedere neproprietă asupra memoriei contigue, înlocuind perechile pointer+mărime- Inițializatoarele desemnate (
{.field = value}) fac construcția structurii să se auto-documenteze
Test — Verifică-ți cunoștințele
(20 XP)1. Ce face`auto operator<=>(const MyType&) const = default;`?
2. Care este avantajul cheie al vizualizărilor Ranges (cum ar fi`std::views::filter`)?
3. Ce problemă rezolvă C++20 Concepts?