Xem trước C++23 & C++26
Khám phá std::expected, std::print của C++23, suy ra điều này, cải tiến phạm vi và xem trước các tính năng C++26 sắp ra mắt như hợp đồng, phản chiếu và khớp mẫu.
Sự tiến hóa tiếp tục
C++23 là bản phát hành "hoàn thiện" nhằm lấp đầy những khoảng trống do C++20 để lại và bổ sung các tính năng được yêu cầu cao. std::expected mang lại khả năng xử lý lỗi theo phong cách Rust, std::print cung cấp đầu ra hiện đại, suy luận này cho phép các mẫu lập trình siêu dữ liệu mạnh mẽ và các phạm vi có được hàng tá chế độ xem mới. Trong khi đó, C++26 đang trở thành một bản phát hành lớn khác với hợp đồng, phản ánh và khớp mẫu — các tính năng về cơ bản sẽ thay đổi cách chúng ta viết mã C++ an toàn, biểu cảm.
std::expected: Xử lý lỗi hiện đại
std::expectedgiữ giá trị thành công thuộc loạiThoặc lỗi thuộc loạiE. Kết hợp với các hoạt động đơn âm (.and_then(),.transform(),.or_else()), nó cho phép xử lý lỗi tổng hợp mà không có ngoại lệ:
#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 và std::println
std::printvàstd::printlnkết hợpstd::formatvới đầu ra, thay thếstd::cout <<cho hầu hết các trường hợp sử dụng. Chúng nhanh hơn, an toàn hơn và tạo ra mã dễ đọc:
#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ải tiến phạm vi: zip, chunk, slide
C++23 bổ sung các chế độ xem phạm vi mới mạnh mẽ bao gồm các mẫu phổ biến trước đây yêu cầu vòng lặp thủ công hoặc thư viện của bên thứ ba:
#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;
}Suy ra điều này
Việc suy ra điều này của C++23 (còn được gọi là "tham số đối tượng rõ ràng") cho phép các hàm thành viên lấythislàm tham số được suy ra rõ ràng. Điều này giúp loại bỏ nhu cầu sao chép quá tảiconst/không phảiconst, cho phép lambda đệ quy và đơn giản hóa 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 hoặc rvalue — và chuyển tiếp chính xác. Mẫu này, được gọi là thành ngữ "suy luận này", có thể cắt giảm một nửa các tập hợp quá tải hàm thành viên.
Xem trước C++26: Hợp đồng, Phản chiếu, Khớp mẫu
C++26 dự kiến sẽ bao gồm một số tính năng biến đổi:
Hợp đồng —pre,postvàcontract_assertcho phép các điều kiện tiên quyết, hậu điều kiện và xác nhận mà hệ thống xây dựng có thể bật hoặc tắt:
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) — Một khung tiêu chuẩn để thực thi không đồng bộ cung cấp tính năng đồng thời có cấu trúc, thay thế việc triển khai nhóm luồng đặc biệt.
Luôn cập nhật sự phát triển của C++:
1. Nhắm mục tiêu tiêu chuẩn tối thiểu cho dự án của bạn (C++17 là cơ sở vững chắc cho hầu hết mã sản xuất hiện nay)
2. Sử dụng cờ trình biên dịch để chọn tiêu chuẩn:-std=c++17,-std=c++20,-std=c++23
3. Kiểm tra sự hỗ trợ của trình biên dịch tại cppreference.com/compiler_support — không phải tất cả các trình biên dịch đều triển khai mọi tính năng
4. Sử dụng macro kiểm tra tính năng như__cpp_concepts,__cpp_lib_expectedđể sử dụng có điều kiện các tính năng mới hơn
5. Đọc các đề xuất (wg21.link/pXXXX) để biết các tính năng bạn dự định áp dụng
6. Đừng theo đuổi mọi tính năng mới - hãy áp dụng những gì làm cho cơ sở mã của bạn đơn giản và chính xác hơn, không chỉ mới hơn
Việc áp dụng các tính năng tiêu chuẩn mới nhất quá sớm sẽ gây ra những tổn thất thực tế:
Lỗi trình biên dịch: Các tính năng mới thường có lỗi triển khai chỉ được phát hiện trong quá trình sử dụng sản xuất. Ví dụ: các mô-đun và coroutine C++20 có lỗi biên dịch nghiêm trọng trong 2-3 năm sau khi chuẩn hóa.
Công cụ chưa hoàn chỉnh: Trình gỡ lỗi, trình phân tích tài nguyên và trình phân tích tĩnh có thể không hỗ trợ đầy đủ các tính năng mới nhất. Hỗ trợ IDE (IntelliSense, hoàn thiện mã) thường bị tụt lại phía sau.
Tính di động: Nếu mã của bạn phải biên dịch trên nhiều nền tảng (Linux, macOS, Windows, nhúng), thì bạn bị giới hạn ở phần giao nhau của những gì tất cả các trình biên dịch đích hỗ trợ.
Sự sẵn sàng của nhóm: Các tính năng mới yêu cầu toàn nhóm tìm hiểu. Một tính năng mà chỉ một nhà phát triển hiểu sẽ tạo ra nút thắt cổ chai trong việc bảo trì.
std::expectedcho phép xử lý lỗi kiểu Rust với chuỗi đơn âm (.and_then(),.transform())std::print/std::printlnthay thếstd::cout <<bằng đầu ra được định dạng sạch, nhanh, an toàn kiểu- Phạm vi C++23 thêm các chế độ xem
zip,chunk,slide,cartesian_productvàenumerate - Việc suy ra điều này giúp loại bỏ sự trùng lặp quá tải hàm const/không phải const
- C++26 nhắm mục tiêu các hợp đồng, phản chiếu tĩnh, khớp mẫu và
std::executioncho tính năng không đồng bộ - Áp dụng các tiêu chuẩn mới dần dần - kiểm tra sự hỗ trợ của trình biên dịch, kiểm tra kỹ lưỡng và đảm bảo sự sẵn sàng của nhóm
Câu đố — Kiểm tra kiến thức
(15 XP)1. `std::expected<int, Error>`đại diện cho điều gì?
2. `std::views::zip(names, scores)`tạo ra cái gì?
3. Tính năng C++26 nào cho phép chỉ định các điều kiện tiên quyết và hậu điều kiện của hàm?