پیشنمایش C++23 و C++26
std::expected، std::print، استنتاج this، بهبودهای محدودهها در C++23، و پیشنمایش ویژگیهای آینده C++26 مانند قراردادها (Contracts)، بازتاب (Reflection)، و تطبیق الگو (Pattern Matching) را کاوش کنید.
تکامل ادامه دارد
C++23 یک نسخه «تکمیلی» است که شکافهای باقیمانده از C++20 را پر میکند و ویژگیهای بسیار درخواستشده را اضافه میکند. std::expected مدیریت خطا به سبک Rust را میآورد، std::print خروجی مدرن فراهم میکند، استنتاج this الگوهای قدرتمند متاپروگرمینگ را ممکن میسازد، و محدودهها دهها نمای جدید دریافت میکنند. در همین حال، C++26 در حال شکلگیری بهعنوان یک نسخه بزرگ دیگر است با قراردادها (Contracts)، بازتاب (Reflection)، و تطبیق الگو (Pattern Matching) — ویژگیهایی که نحوه نوشتن کد 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::print و std::println قالببندی std::format را با خروجی ترکیب میکنند و جایگزین std::cout << برای اکثر موارد استفاده میشوند. سریعتر، ایمن از نظر نوع و تولیدکننده کد خوانا هستند:
#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;
}بهبودهای محدودهها: zip، chunk، slide
C++23 نماهای محدوده جدید و قدرتمندی اضافه میکند که الگوهای رایجی را پوشش میدهند که قبلاً نیاز به حلقههای دستی یا کتابخانههای شخص ثالث داشتند:
#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;
}استنتاج this
ویژگی استنتاج this در C++23 (همچنین «پارامتر صریح شیء» نامیده میشود) به توابع عضو اجازه میدهد this را بهعنوان یک پارامتر صریح و استنتاجشده بگیرند. این نیاز به تکرار سربارگذاریهای const/غیرconst را حذف میکند، لامبداهای بازگشتی را ممکن میسازد، و 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;
}
};پارامتر this Self&& self استنتاج میکند که آیا شیء const، غیرconst، lvalue یا rvalue است — و بهدرستی ارسال (forward) میکند. این الگو، به نام «اصطلاح استنتاج this»، میتواند مجموعه سربارگذاریهای توابع عضو را نصف کند.
پیشنمایش C++26: قراردادها، بازتاب، تطبیق الگو
انتظار میرود C++26 شامل چندین ویژگی تحولآفرین باشد:
قراردادها (Contracts) — pre، post و contract_assert امکان تعیین پیششرطها، پسشرطها و تأییدیههایی را فراهم میکنند که سیستم ساخت میتواند فعال یا غیرفعال کند:
int sqrt_int(int x)
pre(x >= 0)
post(r: r * r <= x)
{
// implementation
}بازتاب ایستا (Static Reflection) — بررسی انواع، اعضا و شمارندهها در زمان کامپایل. تولید کد بر اساس فیلدهای ساختار، استخراج خودکار سریالسازی، و موارد بیشتر — همه بدون ماکرو.
تطبیق الگو (Pattern Matching) — عبارات inspect امکان تطبیق بر روی انواع، مقادیر و ساختار را فراهم میکنند:
inspect (variant_value) {
<int> i => std::println("int: {}", i);
<string> s => std::println("string: {}", s);
__ => std::println("other");
};فرستنده/گیرنده (std::execution) — یک چارچوب استاندارد برای اجرای ناهمگام که همزمانی ساختاریافته فراهم میکند و جایگزین پیادهسازیهای موقتی استخر نخ میشود.
برای بهروز ماندن با تکامل C++:
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 برای مثال، ۲ تا ۳ سال پس از استانداردسازی باگهای مهم کامپایلری داشتند.
ابزارهای ناقص: دیباگرها، پروفایلرها و تحلیلگرهای ایستا ممکن است بهطور کامل از جدیدترین ویژگیها پشتیبانی نکنند. پشتیبانی IDE (IntelliSense، تکمیل کد) اغلب عقب میماند.
قابلیت حمل: اگر کد شما باید روی چندین پلتفرم کامپایل شود (لینوکس، macOS، ویندوز، تعبیهشده)، به اشتراک پشتیبانی تمام کامپایلرهای هدف محدود هستید.
آمادگی تیم: ویژگیهای جدید نیاز به یادگیری در سطح تیم دارند. ویژگیای که فقط یک توسعهدهنده میفهمد، گلوگاه نگهداری ایجاد میکند.
std::expectedمدیریت خطا به سبک Rust با زنجیرهسازی مونادیک (.and_then()،.transform()) را ممکن میسازدstd::print/std::printlnجایگزینstd::cout <<با خروجی قالببندیشده تمیز، سریع و ایمن از نظر نوع میشوند- محدودههای C++23 نماهای
zip،chunk،slide،cartesian_productوenumerateرا اضافه میکنند - استنتاج this تکرار سربارگذاری توابع عضو const/غیر-const را حذف میکند
- C++26 قراردادها، بازتاب ایستا، تطبیق الگو و
std::executionبرای ناهمگام را هدف قرار میدهد - استانداردهای جدید را بهتدریج اتخاذ کنید — پشتیبانی کامپایلر را بررسی کنید، بهدقت تست کنید، و آمادگی تیم را اطمینان حاصل کنید
آزمون — دانش خود را بسنجید
(15 امتیاز)1. `std::expected<int, Error>` چه چیزی را نمایش میدهد؟
2. `std::views::zip(names, scores)` چه چیزی تولید میکند؟
3. کدام ویژگی C++26 امکان تعیین پیششرطها و پسشرطهای تابع را فراهم میکند؟