"typename" और "class" शब्दों के पीछे क्या छिपा है? वे अंतर जो आपको जानना चाहिए
\n\nयदि आपने कभी C++ में टेम्पलेट लिखे हैं, तो आपने निश्चित रूप से दो प्रतीत होने वाले परस्पर विनिमय योग्य कीवर्ड देखे होंगे: typename और class। यह एक छोटी सी बात लगती है, लेकिन Stack Overflow पर इस प्रश्न ने हजारों बार देखा गया है। क्यों? क्योंकि इस स्पष्ट सादगी के पीछे एक ऐसा पत्थर छिपा है जो आपके कोड के संकलन को तोड़ सकता है।
इस लेख में हम एक बार और हमेशा के लिए समझेंगे: C++ टेम्पलेट्स में typename और class के बीच क्या अंतर है, उनका उपयोग कब समानार्थी के रूप में किया जा सकता है, और कब यह त्रुटि का कारण बनेगा। कोई बेकार की बात नहीं - केवल कोड, उदाहरण और स्पष्ट नियम।
\n\nसंक्षिप्त उत्तर: क्या कोई अंतर है भी?
\n\nStack Overflow पर सबसे लोकप्रिय उत्तर यह है: टेम्पलेट पैरामीटर घोषित करने के लिए - कोई अंतर नहीं है। आप लिख सकते हैं:
\n\ntemplate <typename T> void foo(T t);\nया
\ntemplate <class T> void foo(T t);\n\nदोनों विकल्प पूरी तरह से वैध हैं और एक ही काम करते हैं: टेम्पलेट पैरामीटर T घोषित करना। ऐतिहासिक रूप से, class पहले आया (C++98 में), और typename बाद में जोड़ा गया ताकि इस बात पर जोर दिया जा सके कि पैरामीटर केवल एक क्लास ही नहीं, बल्कि कोई भी प्रकार (int, double, पॉइंटर, आदि) हो सकता है।
हालांकि शैतान विवरण में छिपा है। एक ऐसा परिदृश्य है जहाँ typename अनिवार्य है, और class काम नहीं करेगा। और वह है - आश्रित नाम (dependent names)।
मुख्य पत्थर: आश्रित प्रकार (dependent types)
\n\nएक स्थिति की कल्पना करें: आप एक टेम्पलेट लिख रहे हैं, जिसके अंदर आप टेम्पलेट पैरामीटर में निहित एक प्रकार का उपयोग कर रहे हैं। उदाहरण के लिए, आपके पास एक कंटेनर है, और आप उसका इटरेटर प्राप्त करना चाहते हैं:
\n\ntemplate <typename Container>\nvoid printFirst(const Container& c) {\n Container::const_iterator it = c.begin(); // त्रुटि!\n std::cout << *it;\n}\n\nयह संकलित क्यों नहीं होता? क्योंकि Container::const_iterator एक आश्रित नाम है। पार्सिंग के चरण में कंपाइलर नहीं जानता कि Container क्या है। यह एक क्लास हो सकता है, या एक स्थैतिक चर का नाम हो सकता है? डिफ़ॉल्ट रूप से, C++ मानता है कि Container::const_iterator एक चर (या स्थैतिक डेटा सदस्य) का नाम है, न कि एक प्रकार। और केवल कीवर्ड typename कंपाइलर को बताता है: "अरे, यह एक प्रकार है!"।
इसे ठीक करते हैं:
\n\ntemplate <typename Container>\nvoid printFirst(const Container& c) {\n typename Container::const_iterator it = c.begin(); // ठीक है!\n std::cout << *it;\n}\n\nमहत्वपूर्ण: यहाँ class काम नहीं करेगा। class Container::const_iterator लिखना एक सिंटैक्स त्रुटि है। typename ही एकमात्र सही विकल्प है।
typename का उपयोग class के बजाय कब करें?
\n\nएक सरल नियम याद रखें: typename का उपयोग दो मामलों में किया जाता है:
- \n
- टेम्पलेट पैरामीटर के रूप में (
classका पर्याय):template <typename T>\n - यह इंगित करने के लिए कि एक आश्रित नाम एक प्रकार है:
typename T::value_type\n
और class केवल पहले मामले के लिए है। साथ ही, कई C++ प्रोग्रामर पैरामीटर के लिए typename पसंद करते हैं ताकि क्लास घोषणा के साथ भ्रम न हो।
स्पष्टीकरण के साथ पूर्ण उदाहरण
\n\nआइए वास्तविक कोड को समझें जो व्यवहार में C++ टेम्पलेट्स में typename और class के बीच अंतर को प्रदर्शित करता है:
\n\n#include <iostream>\n#include <vector>\n#include <list>\n\n// चरण 1: class (या typename - कोई अंतर नहीं) के साथ टेम्पलेट\ntemplate <class T>\nclass MyContainer {\npublic:\n // चरण 2: आश्रित प्रकार के लिए typename का उपयोग करें\n // T::value_type एक प्रकार है, न कि