C++20: چهار ویژگی بزرگ و بیشتر
ویژگیهای تحولآفرین C++20 را کشف کنید: مفاهیم (Concepts)، محدودهها (Ranges)، عملگر سفینه فضایی، std::format، مقداردهندههای نامگذاری شده، std::span، و موارد بیشتر.
C++20: یک جهش نسلی
C++20 مهمترین بهروزرسانی C++ از زمان C++11 است. چهار ویژگی اصلی را معرفی میکند — مفاهیم (Concepts)، محدودهها (Ranges)، کوروتینها (Coroutines)، و ماژولها (Modules) — که هر کدام نحوه نوشتن کد C++ را اساساً تغییر میدهند. فراتر از «چهار بزرگ»، C++20 همچنین عملگر سفینه فضایی، std::format، std::span، مقداردهندههای نامگذاری شده، و بسیاری از بهبودهای constexpr را به ارمغان میآورد. این درس همه چیز را به جز کوروتینها و ماژولها پوشش میدهد که درسهای اختصاصی خود را دارند.
مفاهیم (Concepts): محدود کردن قالبها
مفاهیم (Concepts) جایگزین SFINAE با محدودیتهای خوانا و ترکیبپذیر بر پارامترهای قالب میشوند. هنگامی که محدودیتها برآورده نشوند، پیامهای خطای واضحی تولید میکنند:
#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;
}محدودهها (Ranges): الگوریتمهای ترکیبپذیر
محدودهها (Ranges) رویکردی تابعی و خطلولهای به STL میآورند. نماها (Views) تنبل هستند — تخصیص حافظه یا کپی انجام نمیدهند و تبدیلهایی را ترکیب میکنند که فقط هنگام پیمایش اجرا میشوند:
#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;
}مقایسه سهطرفه و مقداردهندههای نامگذاری شده
عملگر سفینه فضایی <=> تمام شش عملگر مقایسه را از یک اعلان واحد تولید میکند. مقداردهندههای نامگذاری شده (Designated Initializers) مقداردهی اولیه فیلد نامگذاری شده را از C به C++ با ایمنی نوع اضافه شده میآورند:
#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 و std::span
std::format قالببندی رشتهای شبیه Python را به C++ میآورد. std::span یک نمای غیرمالک بر روی یک دنباله پیوسته از عناصر است که جایگزین جفتهای اشارهگر+اندازه خام میشود:
#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 و Chrono
C++20 بهطور چشمگیری آنچه میتواند constexpr باشد را گسترش میدهد: توابع مجازی، dynamic_cast، try-catch، و حتی std::vector و std::string اکنون میتوانند در زمینههای constexpr استفاده شوند. این بدان معناست که محاسبات بیشتری به زمان کامپایل منتقل میشوند و کد زمان اجرا سریعتر خواهد بود.
کتابخانه یک سیستم کامل تقویم و منطقه زمانی دریافت میکند. میتوانید تاریخهایی مانند 2024y/January/15 را نمایش دهید، حساب تقویمی انجام دهید، و بین مناطق زمانی تبدیل کنید — همه ایمن از نظر نوع و بدون کتابخانههای خارجی.
ویژگیها (Attributes): [[likely]] و [[unlikely]] به کامپایلر راهنمایی میدهند که کدام شاخهها مسیرهای پرتردد هستند و تولید کد بهتری در بخشهای حساس به عملکرد امکانپذیر میسازند.
نیازی نیست همه C++20 را یکباره استفاده کنید. با ویژگیهایی شروع کنید که بیشترین افزایش بهرهوری را با کمترین اختلال ارائه میدهند:
1. عملگر سفینه فضایی — فوراً auto operator<=>(const T&) const = default; را به انواع خود اضافه کنید
2. مفاهیم (Concepts) — از مفاهیم استاندارد (std::integral، std::ranges::range) قبل از نوشتن مفاهیم سفارشی استفاده کنید
3. std::format — جایگزین قالببندی printf و stringstream کنید
4. محدودهها (Ranges) — با std::views::filter و std::views::transform در کد جدید شروع کنید
5. مقداردهندههای نامگذاری شده — برای ساختارهای پیکربندی و انواع گزینهای استفاده کنید
کوروتینها و ماژولها را برای زمانی نگه دارید که تیم و زنجیره ابزار شما آماده باشند.
- مفاهیم (Concepts) جایگزین SFINAE با محدودیتهای خوانا و ترکیبپذیر قالب و پیامهای خطای واضح میشوند
- محدودهها (Ranges) خطوط لوله تنبل و ترکیبپذیر با استفاده از عملگر
|ارائه میدهند — بدون نیاز به ظروف موقتی - عملگر سفینه فضایی
<=>تمام شش عملگر مقایسه را از یک اعلان پیشفرض تولید میکند std::formatقالببندی رشتهای ایمن از نظر نوع و به سبک Python را به C++ میآوردstd::spanیک نمای غیرمالک بر حافظه پیوسته است که جایگزین جفتهای اشارهگر+اندازه میشود- مقداردهندههای نامگذاری شده (
{.field = value}) ساخت ساختار را خودمستندسازی میکنند
آزمون — دانش خود را بسنجید
(20 امتیاز)1. عبارت `auto operator<=>(const MyType&) const = default;` چه کاری انجام میدهد؟
2. مزیت کلیدی نماهای محدودهها (Ranges Views) (مانند `std::views::filter`) چیست؟
3. مفاهیم C++20 (Concepts) چه مشکلی را حل میکنند؟