Προεπισκόπηση C++23 & C++26
Εξερευνήστε τα std::expected, std::print του C++23, συμπερασματικά, βελτιώσεις σειρών και κάντε προεπισκόπηση επερχόμενων λειτουργιών του C++26, όπως συμβόλαια, προβληματισμό και αντιστοίχιση μοτίβων.
Η Εξέλιξη Συνεχίζεται
Το C++23 είναι μια "ολοκληρωτική" έκδοση που καλύπτει τα κενά που αφήνει το C++20 και προσθέτει χαρακτηριστικά που ζητούνται ιδιαίτερα. Το std::expected φέρνει τον χειρισμό σφαλμάτων τύπου Rust, το std::print παρέχει μοντέρνα αποτελέσματα, συμπερασματικά επιτρέπει ισχυρά μοτίβα μεταπρογραμματισμού και οι περιοχές αποκτούν δεκάδες νέες προβολές. Εν τω μεταξύ, το C++26 διαμορφώνεται ως άλλη μια σημαντική κυκλοφορία με συμβόλαια, αναστοχασμό και ταίριασμα μοτίβων — χαρακτηριστικά που θα αλλάξουν ριζικά τον τρόπο με τον οποίο γράφουμε ασφαλή, εκφραστικό κώδικα C++.
std::expected: Σύγχρονος χειρισμός σφαλμάτων
Τοstd::expectedέχει είτε μια τιμή επιτυχίας τύπουTείτε ένα σφάλμα τύπουE. Σε συνδυασμό με μοναδικές λειτουργίες (.and_then(),.transform(),.or_else()), επιτρέπει τη διαχείριση σφαλμάτων με δυνατότητα σύνθεσης χωρίς εξαιρέσεις:
#include <iostream>
#include <expected>
#include <string>
#include <charconv>
#include <system_error>
enum class ParseError {
empty_input,
invalid_format,
out_of_range
};
std::expected<int, ParseError> parse_int(std::string_view sv) {
if (sv.empty()) return std::unexpected(ParseError::empty_input);
int result{};
auto [ptr, ec] = std::from_chars(sv.data(), sv.data() + sv.size(), result);
if (ec == std::errc::invalid_argument)
return std::unexpected(ParseError::invalid_format);
if (ec == std::errc::result_out_of_range)
return std::unexpected(ParseError::out_of_range);
if (ptr != sv.data() + sv.size())
return std::unexpected(ParseError::invalid_format);
return result;
}
std::expected<int, ParseError> double_if_positive(int val) {
if (val <= 0) return std::unexpected(ParseError::out_of_range);
return val * 2;
}
int main() {
// Direct usage
auto result = parse_int("42");
if (result) {
std::cout << "Parsed: " << *result << "\n";
} else {
std::cout << "Error code: " << static_cast<int>(result.error()) << "\n";
}
// Monadic chaining — like Rust's Result::and_then
auto chained = parse_int("21")
.and_then(double_if_positive) // int -> expected<int, E>
.transform([](int v) { return v + 1; }) // int -> int (wrapped)
.or_else([](ParseError e) -> std::expected<int, ParseError> {
std::cout << "Recovering from error\n";
return 0; // default value on error
});
std::cout << "Chained result: " << *chained << "\n"; // 43
return 0;
}std::print και std::println
std::printandstd::printlncombinestd::formatwith output, replacingstd::cout <<for most use cases. They are faster, type-safe, and produce readable code:
#include <print>
#include <vector>
#include <string>
#include <ranges>
int main() {
// Basic printing — no more << chains!
std::println("Hello, {}!", "C++23");
std::print("No newline here. ");
std::println("But here.");
// Formatting works just like std::format
int x = 42;
double pi = 3.14159;
std::println("x = {}, pi = {:.2f}", x, pi);
std::println("Hex: {:#x}, Binary: {:#b}", 255, 255);
// Print to stderr
std::println(stderr, "Warning: something happened");
// Containers and ranges (C++23 formatter support)
std::vector<int> v{1, 2, 3, 4, 5};
std::println("Vector: {}", v); // [1, 2, 3, 4, 5]
// Formatted table
std::println("{:<15} {:>10} {:>10}", "Name", "Score", "Grade");
std::println("{:<15} {:>10} {:>10}", "Alice", 95, "A");
std::println("{:<15} {:>10} {:>10}", "Bob", 87, "B+");
return 0;
}Βελτιώσεις εύρους: φερμουάρ, κομμάτι, διαφάνεια
C++23 adds powerful new range views that cover common patterns previously requiring manual loops or third-party libraries:
#include <print>
#include <ranges>
#include <vector>
#include <string>
int main() {
std::vector<std::string> names{"Alice", "Bob", "Carol"};
std::vector<int> scores{95, 87, 92};
// zip — combine multiple ranges element-wise
for (auto [name, score] : std::views::zip(names, scores)) {
std::println("{}: {}", name, score);
}
std::vector<int> data{1, 2, 3, 4, 5, 6, 7, 8, 9};
// chunk — split into groups of N
for (auto chunk : data | std::views::chunk(3)) {
std::print("[ ");
for (int v : chunk) std::print("{} ", v);
std::println("]");
}
// [ 1 2 3 ] [ 4 5 6 ] [ 7 8 9 ]
// slide — sliding window of size N
for (auto window : data | std::views::slide(3)) {
std::print("( ");
for (int v : window) std::print("{} ", v);
std::println(")");
}
// ( 1 2 3 ) ( 2 3 4 ) ( 3 4 5 ) ... ( 7 8 9 )
// cartesian_product — all combinations
std::vector<char> suits{'H', 'D', 'C', 'S'};
std::vector<int> ranks{1, 2, 3};
for (auto [suit, rank] : std::views::cartesian_product(suits, ranks)) {
std::print("{}{} ", suit, rank);
}
std::println("");
// enumerate — index + value (finally!)
for (auto [i, name] : std::views::enumerate(names)) {
std::println("[{}] {}", i, name);
}
return 0;
}Deducing this
C++23's deducing this (also called "explicit object parameter") lets member functions takethisas an explicit, deduced parameter. This eliminates the need to duplicateconst/non-constoverloads, enables recursive lambdas, and simplifies CRTP:
struct Widget {
std::string name;
// Before C++23: two identical overloads
// const std::string& get_name() const { return name; }
// std::string& get_name() { return name; }
// C++23: one function handles both
template<typename Self>
auto&& get_name(this Self&& self) {
return std::forward<Self>(self).name;
}
};The parameter this Self&& self deduces whether the object is const, non-const, lvalue, or rvalue — and forwards correctly. This pattern, called the "deducing this" idiom, can cut member function overload sets in half.
C++26 Προεπισκόπηση: Συμβάσεις, προβληματισμός, αντιστοίχιση προτύπων
Το C++26 αναμένεται να περιλαμβάνει πολλά μετασχηματιστικά χαρακτηριστικά:
Συμβόλαια — Ταpre,postκαιcontract_assertεπιτρέπουν προϋποθέσεις, εκ των υστέρων και ισχυρισμούς ότι το σύστημα κατασκευής μπορεί να ενεργοποιήσει ή να απενεργοποιήσει:
int sqrt_int(int x)
pre(x >= 0)
post(r: r * r <= x)
{
// implementation
}Static Reflection — Inspect types, members, and enumerators at compile time. Generate code based on struct fields, auto-derive serialization, and more — all without macros.
Pattern Matching — inspect expressions allow matching on types, values, and structure:
inspect (variant_value) {
<int> i => std::println("int: {}", i);
<string> s => std::println("string: {}", s);
__ => std::println("other");
};Sender/Receiver (std::execution) — Ένα τυπικό πλαίσιο για ασύγχρονη εκτέλεση που παρέχει δομημένη ταυτόχρονη χρήση, αντικαθιστώντας ad-hoc εφαρμογές συγκέντρωσης νημάτων.
Παραμένοντας ενημερωμένοι με το C++ evolution:
1. Στοχεύστε ένα ελάχιστο πρότυπο για το έργο σας (το C++17 είναι μια σταθερή βάση για τους περισσότερους κωδικούς παραγωγής σήμερα)
2. Χρησιμοποιήστε σημαίες μεταγλωττιστή για να επιλέξετε το πρότυπο:-std=c++17,-std=c++20,-std=c++23
3. Ελέγξτε την υποστήριξη μεταγλωττιστή στη διεύθυνση cppreference.com/compiler_support — δεν εφαρμόζουν όλοι οι μεταγλωττιστές κάθε δυνατότητα
4. Χρησιμοποιήστε μακροεντολές δοκιμής χαρακτηριστικών όπως__cpp_concepts,__cpp_lib_expectedγια να χρησιμοποιήσετε υπό όρους νεότερες δυνατότητες
5. Διαβάστε τις προτάσεις (wg21.link/pXXXX) για λειτουργίες που σκοπεύετε να υιοθετήσετε
6. Μην κυνηγάτε κάθε νέα δυνατότητα — υιοθετήστε αυτό που κάνει τη βάση κωδίκων σας πιο απλή και σωστή, όχι απλώς νεότερη
Η υιοθέτηση των πιο πρόσφατων τυπικών λειτουργιών πολύ νωρίς έχει πραγματικό κόστος:
Σφάλματα μεταγλωττιστή: Οι νέες δυνατότητες έχουν συχνά σφάλματα υλοποίησης που ανακαλύπτονται μόνο μέσω της χρήσης παραγωγής. Οι ενότητες και οι κορουτίνες C++20, για παράδειγμα, είχαν σημαντικά σφάλματα μεταγλωττιστή για 2-3 χρόνια μετά την τυποποίηση.
Ημιτελής εργαλεία: Τα προγράμματα εντοπισμού σφαλμάτων, τα προφίλ και οι στατικοί αναλυτές ενδέχεται να μην υποστηρίζουν πλήρως τις πιο πρόσφατες δυνατότητες. Η υποστήριξη IDE (IntelliSense, συμπλήρωση κώδικα) συχνά υστερεί.
Φορητότητα: Εάν ο κώδικάς σας πρέπει να μεταγλωττιστεί σε πολλές πλατφόρμες (Linux, macOS, Windows, ενσωματωμένο), περιορίζεστε στη διασταύρωση αυτού που υποστηρίζουν όλοι οι μεταγλωττιστές-στόχοι.
Ετοιμότητα ομάδας: Οι νέες δυνατότητες απαιτούν εκμάθηση σε όλη την ομάδα. Ένα χαρακτηριστικό που κατανοεί μόνο ένας προγραμματιστής δημιουργεί ένα πρόβλημα συντήρησης.
- Το
std::expectedεπιτρέπει τον χειρισμό σφαλμάτων τύπου Rust με μονοαδική αλυσίδα (.and_then(),.transform()) std::print/std::printlnαντικαταστήστε τοstd::cout <<με καθαρή, γρήγορη, ασφαλή για τον τύπο μορφοποιημένη έξοδο- Οι σειρές C++23 προσθέτουν προβολές
zip,chunk,slide,cartesian_productκαιenumerate - Η εξαγωγή αυτού εξαλείφει την επανάληψη υπερφόρτωσης συναρτήσεων συνάρτησης συνάρτησης const/non-const
- Το C++26 στοχεύει συμβόλαια, στατική ανάκλαση, αντιστοίχιση προτύπων και
std::executionγια ασυγχρονισμό - Υιοθετήστε νέα πρότυπα σταδιακά — ελέγξτε την υποστήριξη μεταγλωττιστή, δοκιμάστε διεξοδικά και διασφαλίστε την ετοιμότητα της ομάδας
Κουίζ — Δοκιμάστε τις γνώσεις σας
(15 XP)1. Τι αντιπροσωπεύει το`std::expected<int, Error>`;
2. Τι παράγει το`std::views::zip(names, scores)`;
3. Ποια δυνατότητα C++26 επιτρέπει τον καθορισμό προϋποθέσεων και μετασυνθηκών συνάρτησης;