C++ में Generics और टाइपिंग: यह 'सिर्फ टेम्पलेट' क्यों नहीं है?
\n\nजब कोई डेवलपर Python या Java से C++ पर आता है, तो उसे अक्सर 'टेम्पलेट' (template) शब्द डराता है। दिमाग में अनगिनत कोणीय कोष्ठक, समझ से परे कंपाइलेशन त्रुटियाँ और std::vector<int> का जादू आता है। वास्तव में C++ में generics (या सामान्यीकृत प्रोग्रामिंग) एक बहुत शक्तिशाली उपकरण है जो आपको एक बार कोड लिखने और किसी भी डेटा प्रकार के साथ उपयोग करने की अनुमति देता है। और हाँ, यह Java या C# में जेनेरिक के समान बिल्कुल नहीं है।
इस लेख में, हम समझेंगे कि C++ में टाइपिंग टेम्पलेट के माध्यम से कैसे काम करती है, क्लास टेम्पलेट फंक्शन टेम्पलेट से कैसे भिन्न होते हैं, और C++20 में कॉन्सेप्ट क्यों जोड़े गए। सरल से लेकर प्रोडक्शन में वास्तव में उपयोगी उदाहरणों तक, बहुत सारे उदाहरण होंगे। चलिए शुरू करते हैं!
\n\nC++ में Generics (टेम्पलेट) क्या है?
\n\nबहुत सरल शब्दों में: C++ में टेम्पलेट कोड लिखने का एक तरीका है जो किसी भी डेटा प्रकार के साथ काम करता है, बिना प्रदर्शन खोए। Python की डायनामिक टाइपिंग के विपरीत, जहाँ प्रकार रनटाइम पर जाँचा जाता है, C++ कंपाइलेशन के समय उपयोग किए गए प्रत्येक प्रकार के लिए फंक्शन या क्लास का एक अलग संस्करण उत्पन्न करता है। इसे पैरामीटरीकरण के साथ स्टैटिक टाइपिंग कहा जाता है।
\n\nवाक्यात्मक रूप से, टेम्पलेट को कीवर्ड template और पैरामीटर के साथ कोणीय कोष्ठक का उपयोग करके घोषित किया जाता है:
template <typename T>\nT max(T a, T b) {\n return (a > b) ? a : b;\n}\n\nयहाँ typename T टेम्पलेट का पैरामीटर है। आप typename के बजाय class का उपयोग कर सकते हैं - कोई अंतर नहीं है। जब आप max(3, 7) लिखते हैं, तो कंपाइलर स्वचालित रूप से T के बजाय int डालता है और फंक्शन int max(int, int) बनाता है। यदि आप max(3.14, 2.71) लिखते हैं - तो आपको double के लिए संस्करण मिलता है। सब कुछ सरल और कुशल है।
फंक्शन टेम्पलेट: Generics की ओर पहला कदम
\n\nछोटी शुरुआत करते हैं - C++ में फंक्शन टेम्पलेट। यह कोड दोहराव से बचने का एक आदर्श तरीका है जब तर्क समान हो लेकिन प्रकार अलग हों।
\n\nउदाहरण: एक फंक्शन जो कंसोल पर एक तत्व प्रिंट करता है (डिबगिंग के लिए उपयोगी):
\n\n#include <iostream>\n#include <string>\n\ntemplate <typename T>\nvoid print(const T& value) {\n std::cout << value << std::endl;\n}\n\nint main() {\n print(42); // int\n print(3.1415); // double\n print("Hello"); // const char*\n print(std::string("Generics!")); // std::string\n return 0;\n}\n\nध्यान दें: हम const T& का उपयोग कर रहे हैं - यह C++ में टाइपिंग है जिसमें बड़ी वस्तुओं की प्रतिलिपि बनाने से बचने के लिए कॉन्स्टेंट रेफरेंस द्वारा पास किया जाता है। अंतर्निहित प्रकारों (int, double) के लिए यह महत्वपूर्ण नहीं है, लेकिन यह एक अच्छी आदत है।
क्या होगा यदि आपको विभिन्न प्रकारों के दो तर्क पास करने की आवश्यकता हो? आसान है:
\n\ntemplate <typename T1, typename T2>\nauto add(T1 a, T2 b) -> decltype(a + b) {\n return a + b;\n}\n\n// C++14 और उसके बाद में, आप बस यह कर सकते हैं:\ntemplate <typename T1, typename T2>\nauto add(T1 a, T2 b) {\n return a + b;\n}\n\nयहाँ auto स्वचालित रूप से रिटर्न प्रकार का अनुमान लगाता है। यही generics क्रिया में है - एक कोड, प्रकारों के अनंत संयोजन।
क्लास टेम्पलेट: सार्वभौमिक संरचनाओं का निर्माण
\n\nअब C++ में क्लास टेम्पलेट पर आते हैं। यह मानक पुस्तकालय (STL) का आधार है: std::vector<T>, std::map<Key, Value>, std::optional<T> - ये सभी टेम्पलेट के माध्यम से कार्यान्वित किए गए हैं।
आइए एक सरल लेकिन उदाहरणात्मक उदाहरण लिखें - एक टेम्पलेट क्लास 'पेयर' (std::pair का एनालॉग):
template <typename First, typename Second>\nclass Pair {\nprivate:\n First first_;\n Second second_;\npublic: