विषय पर बढ़ें

💡 सारांश:std::function अंदर से कैसे काम करता है (टाइप इरेज़र, छोटा बफ़र ऑप्टिमाइज़ेशन) उदाहरणों के साथ, और C++23 का std::move_only_function कब std::function की जगह लेता है।

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

C++ std::function विस्तार से

हाल ही में C++ कोड लिखते समय is_function का उपयोग किया, फिर चलते-चलते इसका सोर्स कोड इम्प्लीमेंटेशन देखा और कुछ बातें सामने आईं — पहले हमने तीन डॉट्स ... सीखे थे, तो छह डॉट्स ...... क्या हैं ये पता है?

उसी तरह, अगर ऐसा कोड हो:

template<typename... Args>
struct foo(Args......);

तो क्या पता है इसका मतलब?

आइए गहराई में उतरते हैं!

1.छह डॉट्स

छह डॉट्स को हम आमतौर पर two ellipsis operators कहते हैं, हम छह डॉट्स को तोड़ सकते हैं, जिससे निम्नलिखित तीन स्थितियाँ मिलती हैं

template <typename... Args> void foo1(Args......)
template <typename... Args> void foo2(Args... ...)
template <typename... Args> void foo3(Args..., ...)

अब देखने में आसान है, आइए std::is_function का कोड देखें।

2.is_function इम्प्लीमेंटेशन विवरण

std::is_function का इम्प्लीमेंटेशन primary टेम्पलेट और दो partial specialization से जुड़ा है:

// 主模板,假设提供的类型不是函数类型
template<typename T>
struct is_function : std::false_type {};

// 偏特化,用于正常的函数类型
template<typename Ret, typename... Args>
struct is_function<Ret(Args...)> : std::true_type {};

// 偏特化,用于变参函数类型
template<typename Ret, typename... Args>
struct is_function<Ret(Args......)> : std::true_type {};
  • primary टेम्पलेट मानता है कि दिया गया टाइप फ़ंक्शन टाइप नहीं है, और परिणाम के रूप में std::false_type देता है।
  • पहला partial specialization सामान्य फ़ंक्शन टाइप के लिए है, जिसमें Ret रिटर्न टाइप और Args... पैरामीटर टाइप्स दर्शाता है। अगर दिया गया टाइप इस फ़ंक्शन सिग्नेचर से मैच करता है, तो परिणाम std::true_type होता है।
  • दूसरा partial specialization variadic फ़ंक्शन टाइप के लिए है, जिसमें एलिप्सिस दर्शाता है कि फ़ंक्शन में पैरामीटर्स की संख्या बदल सकती है। इसी तरह, अगर दिया गया टाइप इस फ़ंक्शन सिग्नेचर से मैच करता है, तो परिणाम std::true_type होता है।

3.उपयोग का उदाहरण

आख़िर में std::is_function का एक उपयोग उदाहरण:

#include <iostream>
#include <type_traits>

int func(int x, int y) { return x + y; }
int vararg_func(int x, ...) { return x; }

int main() {
    std::cout << std::boolalpha;
    std::cout << "Is func a function? " << std::is_function<decltype(func)>::value << std::endl;
    std::cout << "Is vararg_func a function? " << std::is_function<decltype(vararg_func)>::value << std::endl;
    return 0;
}

यह आउटपुट देगा:

Is func a function? true
Is vararg_func a function? true

क्या सीख लिया, इस सेक्शन का अंत~

C++23 std::move_only_function vs std::function

आपने अभी पढ़ा टाइप इरेज़र की एक छिपी कीमत है: std::function माँगता है कि लिपटा हुआ invocable कॉपी-कंस्ट्रक्टिबल हो — कॉपी करने लायक रैपर उसके अनुबंध का हिस्सा है। lambda में std::unique_ptr कैप्चर करके स्टोर करने की कोशिश करें — कंपाइलर मना कर देगा:

auto make_sink() {
    auto ptr = std::make_unique<std::vector<int>>(42);
    return [ptr = std::move(ptr)] { return ptr->size(); };  // move-only closure
}

// std::function<std::size_t()> f = make_sink();   // ERROR: closure not copyable
std::move_only_function<std::size_t()> g = make_sink();  // OK since C++23

std::move_only_function (C++23, हेडर ) कॉपी की शर्त हटा देता है। यह move-only closures स्वीकार करता है, इसलिए पाइपलाइनों, async कार्यों और फ़ाइल हैंडल या कनेक्शन जैसे अनन्य संसाधनों वाले callbacks के लिए यही सही टाइप है।

कैसे चुनें: invocable कॉपी करना सस्ता हो और पुराने APIs माँगते हों तो std::function रखें; closure किसी अनन्य चीज़ की मालिक हो तो std::move_only_function पर जाएँ। यह भी याद रखें कि std::function असाइनमेंट पर closure कॉपी करता है — भारी कैप्चर पर यह असली लागत है, जिससे move_only_function बचाता है। परफ़ॉर्मेंस-संवेदनशील जगहों पर सादा टेम्पलेट पैरामीटर दोनों से आगे है: न इरेज़र, न अप्रत्यक्ष कॉल, पूरी तरह inline।

अक्सर पूछे जाने वाले प्रश्न

std::function और std::move_only_function में क्या अंतर है?

std::function कॉपी करने योग्य invocables माँगता है और असाइनमेंट पर कॉपी करता है; std::move_only_function (C++23) move-only invocables लेता है और सिर्फ़ मूव करता है। बाक़ी व्यवहार और कॉल सिंटैक्स एक जैसे हैं।

std::function unique_ptr कैप्चर करने वाली lambda क्यों नहीं रख पाता?

क्योंकि std::function को संग्रहीत invocable की कॉपी बनाने की क्षमता चाहिए, और unique_ptr जैसा move-only टाइप कैप्चर होते ही closure का कॉपी कंस्ट्रक्टर डिलीट हो जाता है। std::move_only_function या टेम्पलेट पैरामीटर इस्तेमाल करें।

क्या std::move_only_function, std::function से तेज़ है?

यह कॉपी का ख़र्च और कुछ बहीखाता बचाता है, पर दोनों टाइप इरेज़र और अप्रत्यक्ष कॉल से गुज़रते हैं। अगर कॉल साइट टाइप जानती है, तो invocable को टेम्पलेट पैरामीटर बनाएँ — वही असली तेज़ रास्ता है।

संबंधित विषय

टिप्पणियाँ