आधिकारिक तौर पर, टाइपनेम के लिए क्या है?


131

इस अवसर पर मैंने कुछ बहुत ही अशोभनीय त्रुटि संदेश देखे हैं gccजब टेम्प्लेट का उपयोग करके थूक दिया जाता है ... विशेष रूप से, मुझे ऐसी समस्याएँ हुई हैं जहाँ प्रतीत होता है कि सही घोषणाएँ बहुत ही अजीब संकलन त्रुटियों का कारण बन रही थीं, जो कि typenameकीवर्ड को शुरुआत में कीवर्ड के साथ जोड़कर जादुई रूप से दूर हो गए थे। घोषणा ... (उदाहरण के लिए, अभी पिछले हफ्ते, मैं दो पुनरावृत्तियों को एक और टेम्पर्ड क्लास के सदस्य के रूप में घोषित कर रहा था और मुझे ऐसा करना पड़ा) ...

कहानी क्या है typename?


जवाबों:


207

जोसुतिस की पुस्तक का उद्धरण इस प्रकार है:

यह typenameबताने के लिए कि यह एक प्रकार है पहचानकर्ता को कीवर्ड पेश किया गया था। निम्नलिखित उदाहरण पर विचार करें:

template <class T>
Class MyClass
{
  typename T::SubType * ptr;
  ...
};

यहाँ, typenameयह स्पष्ट करने के लिए प्रयोग किया जाता है कि SubTypeएक प्रकार का है class T। इस प्रकार, प्रकार के ptrलिए एक सूचक है T::SubType। इसके बिना typename, SubType एक स्थिर सदस्य माना जाएगा। इस प्रकार

T::SubType * ptr

के साथ SubTypeप्रकार के मूल्य का गुणन होगा ।Tptr


2
महान पुस्तक। इसे एक बार जरूर पढ़ें और यदि आप चाहें तो इसे एक संदर्भ के रूप में रखें।
deft_code

1
सूक्ष्म पाठक यह महसूस करेगा कि सदस्य घोषणा के लिए व्याकरण द्वारा गुणन अभिव्यक्ति की अनुमति नहीं है। इस प्रकार, C ++ 20 इस की आवश्यकता के साथ फैलता हैtypename (हालांकि उनमें से सभी नहीं!)।
डेविस हेरिंग

मुझे मना नहीं किया। एक बार जब टेम्प्लेट को
त्वरित

36

स्टेन लिपमैन का बीएलओजी पद :

स्ट्रॉस्ट्रुप ने एक नए कीवर्ड को पेश करने के बजाय एक प्रकार के पैरामीटर को निर्दिष्ट करने के लिए मौजूदा वर्ग के कीवर्ड का पुन: उपयोग किया, जो मौजूदा कार्यक्रमों को निश्चित रूप से तोड़ सकता है। ऐसा नहीं था कि एक नए कीवर्ड पर विचार नहीं किया गया था - सिर्फ इसलिए कि आवश्यक विघटन को देखते हुए इसे आवश्यक नहीं माना गया था। और आईएसओ-सी ++ मानक तक, यह एक प्रकार का पैरामीटर घोषित करने का एकमात्र तरीका था।

इसलिए मूल रूप से स्ट्रॉस्ट्रुप ने एक नए कीवर्ड की शुरुआत किए बिना कक्षा के कीवर्ड का पुन: उपयोग किया, जो निम्नलिखित कारणों से मानक में बाद में बदला गया है

जैसा उदाहरण दिया गया है

template <class T>
class Demonstration {
public:
void method() {
    T::A *aObj; // oops …
     // …
};

भाषा व्याकरण T::A *aObj;एक अंकगणितीय अभिव्यक्ति के रूप में गलत व्याख्या करता है इसलिए एक नया कीवर्ड पेश किया जाता हैtypename

typename T::A* a6;

यह संकलक को बाद के बयान को घोषणा के रूप में मानने का निर्देश देता है।

चूंकि कीवर्ड पेरोल पर था, इसलिए बिल्ली, क्लास के कीवर्ड के पुन: उपयोग के मूल निर्णय के कारण उत्पन्न भ्रम को ठीक नहीं करता है।

इसलिए हम दोनों के पास है

आप इस पोस्ट पर एक नज़र डाल सकते हैं , यह निश्चित रूप से आपकी मदद करेगा, मैंने इसे केवल उतना ही निकाला है जितना मैं कर सकता था


हां, लेकिन तब नया कीवर्ड typenameआवश्यक क्यों था , यदि आप classउसी उद्देश्य के लिए मौजूदा कीवर्ड का उपयोग कर सकते हैं ?
जेसपर

5
@ जैस्पर: मुझे लगता है कि ज़ेनस का जवाब यहाँ भ्रमित करने वाला है। typenameजोसुतिस के हवाले से नवीन के जवाब में वर्णित पार्सिंग मुद्दे को ठीक करना आवश्यक हो गया। (मुझे नहीं लगता कि classइस स्थान पर कोई काम करना होगा।) इस मामले के लिए नया कीवर्ड स्वीकार किए जाने के बाद ही, इसे टेम्पलेट तर्क घोषणाओं में भी अनुमति दी गई थी ( या यह परिभाषा है? ), क्योंकि classवहाँ हमेशा कुछ हद तक होता है? भ्रामक।
sbi

13

कोड पर विचार करें

template<class T> somefunction( T * arg )
{
    T::sometype x; // broken
    .
    .

दुर्भाग्य से, कंपाइलर को मानसिक होने की आवश्यकता नहीं है, और यह नहीं जानता कि क्या टी :: किसी प्रकार का नाम या टी के स्थैतिक सदस्य का उल्लेख करते हुए समाप्त हो जाएगा। इसलिए, एक typenameयह बताने के लिए उपयोग करता है:

template<class T> somefunction( T * arg )
{
    typename T::sometype x; // works!
    .
    .

6

कुछ स्थितियों में, जहाँ आप तथाकथित निर्भर प्रकार के सदस्य को संदर्भित करते हैं (जिसका अर्थ है "टेम्पलेट पैरामीटर पर निर्भर"), संकलक हमेशा परिणामी निर्माण के शब्दार्थ अर्थ को कम नहीं कर सकता है, क्योंकि यह नहीं जानता कि यह किस प्रकार का नाम है (यानी चाहे वह किसी प्रकार का नाम हो, डेटा सदस्य का नाम हो या किसी और चीज़ का नाम हो)। इस तरह के मामलों में आपको संकलक को स्पष्ट रूप से यह बताकर स्थिति को भंग करना होगा कि नाम उस आश्रित प्रकार के सदस्य के रूप में परिभाषित टाइपनाम से संबंधित है।

उदाहरण के लिए

template <class T> struct S {
  typename T::type i;
};

इस उदाहरण typenameमें कोड को संकलित करने के लिए आवश्यक कीवर्ड ।

यही बात तब होती है जब आप एक प्रकार के आश्रित सदस्य को संदर्भित करना चाहते हैं, अर्थात एक नाम जो टेम्पलेट तैयार करता है। आपको कीवर्ड का उपयोग करके कंपाइलर की भी मदद करनी होगी template, हालांकि इसे अलग तरह से रखा गया है

template <class T> struct S {
  T::template ptr<int> p;
};

कुछ मामलों में दोनों का उपयोग करना आवश्यक हो सकता है

template <class T> struct S {
  typename T::template ptr<int>::type i;
};

(अगर मुझे सही ढंग से वाक्यविन्यास मिला है)।

बेशक, कीवर्ड की एक और भूमिका typenameटेम्पलेट पैरामीटर घोषणाओं में उपयोग की जानी है।


अधिक (पृष्ठभूमि) जानकारी के लिए C ++ टाइपनेम कीवर्ड का एक विवरण भी देखें ।
अताफर

5

रहस्य इस तथ्य में निहित है कि एक टेम्पलेट कुछ प्रकारों के लिए विशेष हो सकता है। इसका मतलब यह है कि यह इंटरफ़ेस को कई प्रकारों के लिए पूरी तरह से अलग भी परिभाषित कर सकता है। उदाहरण के लिए आप लिख सकते हैं:

template<typename T>
struct test {
    typedef T* ptr;
};

template<>         // complete specialization 
struct test<int> { // for the case T is int
    T* ptr;
};

कोई यह पूछ सकता है कि यह क्यों उपयोगी है और वास्तव में: यह वास्तव में बेकार लग रहा है। लेकिन ध्यान रखें कि उदाहरण std::vector<bool>के referenceलिए अन्य Tएस की तुलना में प्रकार पूरी तरह से अलग दिखता है । माना जाता है कि यह referenceएक प्रकार से कुछ अलग करने के तरीके को नहीं बदलता है लेकिन फिर भी ऐसा हो सकता है।

अब क्या होगा अगर आप इस testटेम्पलेट का उपयोग करके अपने स्वयं के टेम्प्लेट लिखते हैं । कुछ इस तरह

template<typename T>
void print(T& x) {
    test<T>::ptr p = &x;
    std::cout << *p << std::endl;
}

यह आप के लिए ठीक हो सकता है क्योंकि आप लगता है उम्मीद है कि test<T>::ptrएक प्रकार है। लेकिन संकलक को पता नहीं है और विलेख में उसे मानक की सलाह दी जाती है कि वह विपरीत की अपेक्षा करे, test<T>::ptrएक प्रकार नहीं है। संकलक को यह बताने के लिए कि आपको क्या उम्मीद है कि आपको typenameपहले जोड़ना होगा । सही टेम्प्लेट इस तरह दिखता है

template<typename T>
void print(T& x) {
    typename test<T>::ptr p = &x;
    std::cout << *p << std::endl;
}

नीचे पंक्ति: typenameजब भी आप अपने टेम्पलेट में किसी नेस्टेड प्रकार का उपयोग करते हैं, तो आपको पहले जोड़ना होगा । (निश्चित रूप से केवल अगर आपके टेम्पलेट का एक पैरामीटर उस आंतरिक टेम्पलेट के लिए उपयोग किया जाता है।)


5

दो उपयोग:

  1. एक के रूप में templateतर्क कीवर्ड (बजाय class)
  2. एक typenameकीवर्ड कंपाइलर को बताता है कि एक पहचानकर्ता एक प्रकार है (स्थिर सदस्य चर के बजाय)
template <typename T> class X  // [1]
{
    typename T::Y _member;  // [2] 
}

4

मुझे लगता है कि सभी उत्तरों ने उल्लेख किया है कि typenameकीवर्ड का उपयोग दो अलग-अलग मामलों में किया जाता है:

क) टेम्पलेट प्रकार पैरामीटर की घोषणा करते समय। जैसे

template<class T> class MyClass{};        // these two cases are
template<typename T> class MyNewClass{};  // exactly the same.

जो उनके बीच कोई अंतर नहीं है और वे बिल्कुल समान हैं।

b) टेम्पलेट के लिए नेस्टेड डिपेंडेंट टाइप नाम का उपयोग करने से पहले ।

template<class T>
void foo(const T & param)
{
   typename T::NestedType * value; // we should use typename here
}

जिसका उपयोग नहीं करने typenameसे पार्सिंग / संकलन त्रुटियां होती हैं।

स्कॉट मेयर्स बुक इफेक्टिव C ++ में उल्लेखित मैं दूसरे मामले में जो जोड़ना चाहता हूं , वह यह है कि नेस्टेड डिपेंडेंट टाइप नामtypename से पहले उपयोग करने का एक अपवाद है । अपवाद यह है कि यदि आप नेस्टेड आश्रित प्रकार के नाम का उपयोग या तो आधार वर्ग के रूप में या सदस्य प्रारंभ सूची में करते हैं , तो आपको वहां उपयोग नहीं करना चाहिए :typename

template<class T>
class D : public B<T>::NestedType               // No need for typename here
{
public:
   D(std::string str) : B<T>::NestedType(str)   // No need for typename here
   {
      typename B<T>::AnotherNestedType * x;     // typename is needed here
   }
}

नोट: का उपयोग करते हुए typenameदूसरे मामले के लिए (यानी नेस्टेड निर्भर प्रकार नाम से पहले) सी के बाद से ++ 20 की जरूरत नहीं है।


2
#include <iostream>

class A {
public:
    typedef int my_t;
};

template <class T>
class B {
public:
    // T::my_t *ptr; // It will produce compilation error
    typename T::my_t *ptr; // It will output 5
};

int main() {
    B<A> b;
    int my_int = 5;
    b.ptr = &my_int;
    std::cout << *b.ptr;
    std::cin.ignore();
    return 0;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.