सामग्री पर जाएं

C++20: बिग फ़ोर और अन्य सुविधाएँ

C++20 की क्रांतिकारी सुविधाओं को जानें: कॉन्सेप्ट, रेंज, स्पेसशिप ऑपरेटर, std::format, डेज़िग्नेटेड इनिशियलाइज़र, std::span और बहुत कुछ।

C++20: एक पीढ़ीगत छलाँग

C++20, C++11 के बाद C++ का सबसे महत्वपूर्ण अपडेट है। यह चार प्रमुख सुविधाएँ लाता है — कॉन्सेप्ट (Concepts), रेंज (Ranges), कोरूटीन (Coroutines) और मॉड्यूल (Modules) — जिनमें से हर एक C++ कोड लिखने के तरीक़े को बुनियादी रूप से बदल देती है। "बिग फ़ोर" के अलावा C++20 में स्पेसशिप ऑपरेटर, std::format, std::span, डेज़िग्नेटेड इनिशियलाइज़र और constexpr के कई सुधार भी हैं। इस लेसन में कोरूटीन और मॉड्यूल को छोड़कर बाकी सब कुछ शामिल है, क्योंकि उनके लिए अलग लेसन हैं।

कॉन्सेप्ट: टेम्पलेट पर बंदिशें लगाना

कॉन्सेप्ट SFINAE की जगह टेम्पलेट पैरामीटर पर पढ़ने योग्य और जोड़े जा सकने वाले (composable) कंस्ट्रेंट देते हैं। जब कंस्ट्रेंट पूरे नहीं होते, तो ये साफ़ एरर मैसेज देते हैं:

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;
}

रेंज: जोड़े जा सकने वाले एल्गोरिदम

रेंज STL में फ़ंक्शनल, पाइपलाइन-शैली का तरीक़ा लाती हैं। व्यू लेज़ी होते हैं — वे डेटा को न तो आवंटित करते हैं और न कॉपी, बल्कि ऐसे ट्रांसफ़ॉर्मेशन जोड़ते हैं जो केवल इटरेट किए जाने पर ही चलते हैं:

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;
}

थ्री-वे कंपैरिज़न और डेज़िग्नेटेड इनिशियलाइज़र

स्पेसशिप ऑपरेटर <=> एक ही डिक्लेरेशन से सभी छह कंपैरिज़न ऑपरेटर जनरेट कर देता है। डेज़िग्नेटेड इनिशियलाइज़र नामित फ़ील्ड इनिशियलाइज़ेशन को 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 C++ में Python जैसी स्ट्रिंग फ़ॉर्मेटिंग लाता है। std::span तत्वों के एक सन्निहित (contiguous) अनुक्रम पर गैर-स्वामित्व वाला व्यू है, जो रॉ पॉइंटर+साइज़ की जोड़ियों की जगह लेता है:

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 जैसी तारीख़ें दर्शा सकते हैं, कैलेंडर अंकगणित कर सकते हैं और टाइम ज़ोन के बीच बदलाव कर सकते हैं — सब कुछ टाइप-सेफ़ और बिना किसी बाहरी लाइब्रेरी के।

एट्रिब्यूट: [[likely]] और [[unlikely]] कंपाइलर को संकेत देते हैं कि कौन-सी ब्रांच हॉट पाथ हैं, जिससे परफ़ॉर्मेंस-क्रिटिकल हिस्सों में बेहतर कोड जनरेशन संभव होता है।

सर्वोत्तम अभ्यास

आपको C++20 की सभी सुविधाएँ एक साथ इस्तेमाल करने की ज़रूरत नहीं है। उन सुविधाओं से शुरू करें जो सबसे कम व्यवधान में सबसे ज़्यादा प्रोडक्टिविटी देती हैं:

1. स्पेसशिप ऑपरेटर — अपने टाइप में तुरंत auto operator<=>(const T&) const = default; जोड़ें
2. कॉन्सेप्ट — कस्टम कॉन्सेप्ट लिखने से पहले स्टैंडर्ड कॉन्सेप्ट (std::integral, std::ranges::range) इस्तेमाल करें
3. std::format — printf और stringstream फ़ॉर्मेटिंग की जगह इसका उपयोग करें
4. रेंज — नए कोड में std::views::filter और std::views::transform से शुरुआत करें
5. डेज़िग्नेटेड इनिशियलाइज़र — कॉन्फ़िगरेशन स्ट्रक्ट और ऑप्शन टाइप के लिए उपयोग करें

कोरूटीन और मॉड्यूल तब के लिए रखें जब आपकी टीम और टूलचेन तैयार हों।

मुख्य बातें
  • कॉन्सेप्ट SFINAE की जगह पढ़ने योग्य, जोड़े जा सकने वाले टेम्पलेट कंस्ट्रेंट और साफ़ एरर मैसेज देते हैं
  • रेंज | ऑपरेटर के ज़रिए लेज़ी, जोड़ी जा सकने वाली पाइपलाइन देती हैं — अस्थायी कंटेनर की ज़रूरत नहीं
  • स्पेसशिप ऑपरेटर <=> एक डिफ़ॉल्टेड डिक्लेरेशन से सभी छह कंपैरिज़न ऑपरेटर जनरेट करता है
  • std::format C++ में टाइप-सेफ़, Python-शैली की स्ट्रिंग फ़ॉर्मेटिंग लाता है
  • std::span सन्निहित मेमोरी पर गैर-स्वामित्व वाला व्यू है, जो पॉइंटर+साइज़ की जोड़ियों की जगह लेता है
  • डेज़िग्नेटेड इनिशियलाइज़र ({.field = value}) स्ट्रक्ट कंस्ट्रक्शन को स्वयं-दस्तावेज़ी बनाते हैं

क्विज़ — अपना ज्ञान परखें

(20 XP)

1. `auto operator<=>(const MyType&) const = default;` क्या करता है?

2. रेंज व्यू (जैसे `std::views::filter`) का मुख्य लाभ क्या है?

3. C++20 कॉन्सेप्ट किस समस्या को हल करते हैं?