رفتن به محتوا

‎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 با محدودیت‌های خوانا و ترکیب‌پذیر بر پارامترهای قالب می‌شوند. هنگامی که محدودیت‌ها برآورده نشوند، پیام‌های خطای واضحی تولید می‌کنند:

concepts.cpp
#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) تنبل هستند — تخصیص حافظه یا کپی انجام نمی‌دهند و تبدیل‌هایی را ترکیب می‌کنند که فقط هنگام پیمایش اجرا می‌شوند:

ranges.cpp
#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++‎ با ایمنی نوع اضافه شده می‌آورند:

spaceship.cpp
#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 یک نمای غیرمالک بر روی یک دنباله پیوسته از عناصر است که جایگزین جفت‌های اشاره‌گر+اندازه خام می‌شود:

format_span.cpp
#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) چه مشکلی را حل می‌کنند؟