विषय पर बढ़ें

अंतिम अद्यतन:2026-10-12

C++20 std::span डिज़ाइन और जाल

0. अभिप्राय

पारंपरिक C/C++ कोड में, हम प्रायः array तक पहुँचने के लिए एक pointer + size का उपयोग करते हैं, जैसे:

void print(int* arr, int sz) {
    for (int i = 0; i < sz; i++) {
        // todo
    }
}

span इस ऑपरेशन को सरल बना देता है, हो जाता है:

void print(std::span<int> arr) {
    for (auto x : arr) {
        // todo
    }
}

तो आख़िर span क्या है, इसके उपयोग के परिदृश्य और जाल कौन से हैं? इस सेक्शन में विस्तृत चर्चा करते हैं।

1. span क्या है?

C++20 में, span एक हल्का, range-प्रकार का कंटेनर है, जो एक लगातार मेमोरी (contiguous memory) तक पहुँच देता है। span एक view के रूप में, डेटा का स्वामी नहीं होता, बल्कि डेटा का एक सरल रेफ़रेंस है, और प्रायः पारंपरिक C-स्टाइल array या pointer की जगह उपयोग होता है। यह किसी भी प्रकार के array और स्टैंडर्ड कंटेनर, जैसे std::vector, को रख सकता है, और इन डेटा को संसाधित करने का अधिक सुरक्षित और आधुनिक तरीका देता है।

इसमें कुछ मुख्य बिंदु:

  • लगातार मेमोरी (contiguous memory)
  • डेटा का स्वामी नहीं
  • रेफ़रेंस

span का सोर्स कोड gcc के libstdc++-v3/include/std/span में स्थित है।

template <typename _Type, size_t _Extent = dynamic_extent>
class span {};

span प्रकार में दो महत्वपूर्ण पैरामीटर शामिल हैं:

  • _Type: element का प्रकार।
  • Extent: array का आकार। यदि dynamic_extent (डिफ़ॉल्ट मान) है, तो view का आकार dynamically बदल सकता है।

2. span का dynamic और static रूप

span के दो मोड हैं: static और dynamic।

2.1 Static span

Static span का आकार compile समय निर्धारित होता है, इसका Extent एक constant मान है। Static span का उपयोग करते समय, हमें compile समय पर डेटा का आकार पता होना चाहिए।

उदाहरण के लिए:

int arr[5] = {1, 2, 3, 4, 5};
std::span<int, 5> s(arr);  // 静态 span,大小为 5

Static span का आकार compile समय पर निश्चित होता है, इसलिए कंपाइलर कुछ optimization कर सकता है, जैसे bounds checking का छोड़ना। लेकिन चूंकि Extent constant है, static span का आकार runtime पर नहीं बदला जा सकता।

2.2 Dynamic span

Dynamic span का आकार runtime पर निर्धारित होता है, Extent डिफ़ॉल्ट रूप से dynamic_extent है। इस प्रकार का span compile समय पर आकार निर्धारित करने की मांग नहीं करता, यह किसी भी आकार के array या कंटेनर के अनुकूल हो सकता है।

उदाहरण के लिए:

int arr[5] = {1, 2, 3, 4, 5};
std::span<int> s(arr);  // 动态 span,大小在运行时确定

Dynamic span उन स्थितियों के लिए उपयुक्त है जहाँ आकार compile समय पर निर्धारित नहीं हो सकता, यह अधिक लचीलापन देता है।

gcc सोर्स में इसका क्रियान्वयन काफ़ी सरल है, डिफ़ॉल्ट में यह बहुत बड़ा पूर्णांक है (-1 का unsigned रूप), वास्तविक गणना construction के समय होती है।

inline constexpr size_t dynamic_extent = static_cast<size_t>(-1);

उदाहरण के लिए: यहाँ आंतरिक आकार सेट करने के लिए ranges::size का उपयोग होगा, विवरण नीचे दिए कोड में।

std::vector<int> data{1, 2, 3};
std::span<int> s(data);

Dynamic आकार सेट करना।

span(ranges::data(__range), ranges::size(__range))

3. subspan

span एक बहुत उपयोगी मेंबर फ़ंक्शन subspan देता है, जो हमें किसी मौजूदा span से बिना डेटा copy किए एक sub-view निकालने की अनुमति देता है। यह बड़े डेटा सेट से निपटते समय बहुत उपयुक्त है, अनावश्यक मेमोरी ख़र्च से बचा जा सकता है।

subspan() विधि के दो overload हैं:

  1. एक __offset और __count को पैरामीटर की तरह लेता है, जहाँ __offset प्रारंभिक स्थिति है, और __count sub-range का आकार (डिफ़ॉल्ट dynamic_extent, यानी मूल span के अंत तक)।
  2. दूसरा बिना पैरामीटर वाला, टेम्प्लेट पर आधारित क्रियान्वयन।
constexpr auto
subspan() const noexcept
-> span<element_type, _S_subspan_extent<_Offset, _Count>()> {}

template<size_t _Offset, size_t _Count = dynamic_extent>
[[nodiscard]]
constexpr auto
subspan() const noexcept
-> span<element_type, _S_subspan_extent<_Offset, _Count>()> {}

उदाहरण 1:

std::span<int> s(arr);  
std::span<int> sub = s.subspan(1, 3);

उदाहरण 2:

std::span<int> s(arr);  
auto sub = s.subspan<2, 2>();

4. उपयोग के जाल

यद्यपि span बहुत उपयोगी टूल है, उपयोग करते समय कुछ आम जालों पर ध्यान देना चाहिए:

4.1 span को रिलीज़ हो चुकी मेमोरी का रेफ़रेंस मत दें

span खुद डेटा का स्वामी नहीं है, यह केवल डेटा का रेफ़रेंस है। इसलिए, यदि span द्वारा संदर्भित मेमोरी रिलीज़ हो जाए (उदाहरण के लिए मूल array नष्ट हो जाए या scope से बाहर हो जाए), तो उस span तक पहुँच undefined behavior लाएगी।

उदाहरण के लिए:

std::span<int> create_span() {
    int arr[5] = {1, 2, 3, 4, 5};
    return std::span<int>(arr, 5);
}

// 错误:返回的 span 引用了栈上临时变量,函数返回后 arr 被销毁
auto s = create_span();

इस समस्या से बचने के लिए, सुनिश्चित करें कि span द्वारा संदर्भित डेटा उसके जीवनचक्र में वैध रहे। डेटा को dynamically आवंटित मेमोरी में रखने पर विचार करें, या सुनिश्चित करें कि डेटा का जीवनचक्र और span का जीवनचक्र एक समान रहे।

4.2 अंतर्निहित array का आकार बदलने की समस्या

span का अंतर्निहित डेटा का रेफ़रेंस सीधा है, copy नहीं। जब अंतर्निहित डेटा बदलता है, तो span खुद इन परिवर्तनों को महसूस नहीं करता। इसलिए, अंतर्निहित array या कंटेनर का आकार बदलते समय (उदाहरण के लिए मेमोरी पुनः आवंटित करके), पुराना span अमान्य हो सकता है।

std::vector<int> vec = {1, 2, 3, 4, 5};
std::span<int> s(vec);  // s 引用 vec 的数据
// 改变底层容器的大小
vec.push_back(6);  // vec 大小变为 6

// 这里 s 仍然引用 vec 的数据,但 vec 可能已经重新分配了内存
// 这可能导致 s 引用失效或未定义行为

संबंधित विषय

टिप्पणियाँ