C ++ टेम्पलेट पैरामीटर को उपवर्ग में प्रतिबंधित करें


80

मैं Tकिसी विशिष्ट वर्ग के उपवर्ग होने के लिए टेम्पलेट पैरामीटर को कैसे बाध्य कर सकता हूं Baseclass? कुछ इस तरह:

template <class T : Baseclass> void function(){
    T *object = new T();

}

3
आप यह करके क्या हासिल करने की कोशिश कर रहे हैं?
sth

2
मैं सिर्फ यह सुनिश्चित करना चाहता हूं कि टी वास्तव में एक उपवर्ग या स्वयं कक्षा का एक उदाहरण है। मेरे द्वारा प्रदान किए गए फ़ंक्शन के अंदर का कोड बहुत अधिक अप्रासंगिक है।
फैंटम ०

6
इसके विपरीत, यह बहुत प्रासंगिक है। यह निर्धारित करता है कि यह एक अच्छा विचार है या उस परीक्षण में काम नहीं करना है। कई (सभी?) मामलों में, ऐसे अवरोधों को स्वयं लागू करने की बिल्कुल आवश्यकता नहीं है, बल्कि संकलित करते समय संकलक को ऐसा करने दें। उदाहरण के लिए, स्वीकृत उत्तर के लिए, इस बात की जांच करना अच्छा होगा कि क्या Tव्युत्पन्न है Baseclass। अब तक, वह चेक निहित है, और अधिभार संकल्प के लिए दिखाई नहीं दे रहा है। लेकिन अगर कहीं भी ऐसा निहितार्थ नहीं किया गया है, तो कृत्रिम प्रतिबंध का कोई कारण नहीं प्रतीत होता है।
जोहान्स शाउब -

1
हाँ मैं सहमत हूँ। हालांकि, मैं सिर्फ यह जानना चाहता था कि क्या इसे पूरा करने का कोई तरीका है या नहीं :) लेकिन निश्चित रूप से, आपके पास एक बहुत ही मान्य बिंदु है और अंतर्दृष्टि के लिए धन्यवाद।
प्रेत

जवाबों:


53

इस मामले में आप यह कर सकते हैं:

template <class T> void function(){
    Baseclass *object = new T();

}

यह संकलित नहीं करेगा यदि T, बास्कल का उपवर्ग नहीं है (या T , बास्कल है)।


आह हाँ, यह एक अच्छा विचार है। धन्यवाद! मैं इसे लेता हूं तो इसे टेम्पलेट परिभाषा में परिभाषित करने का कोई तरीका नहीं है?
फैंटम ०

2
@ phant0m: सही है। आप स्पष्ट रूप से टेम्पलेट मापदंडों को सीमित नहीं कर सकते हैं (अवधारणाओं का उपयोग करने के अलावा, जिन्हें सी ++ 0x के लिए माना जाता था लेकिन फिर गिरा दिया गया था)। सभी बाधाएं आपके द्वारा उस पर किए जाने वाले कार्यों से निहित होती हैं (या दूसरे शब्दों में एकमात्र बाधा यह है कि "प्रकार को उन सभी कार्यों का समर्थन करना चाहिए जो उस पर किए जाते हैं")।
sepp2k

1
आह आइ सी। स्पष्टीकरण के लिए बहुत बहुत धन्यवाद!
प्रेत

8
जो T () कंस्ट्रक्टर को निष्पादित करता है, और T () कंस्ट्रक्टर के अस्तित्व की आवश्यकता होती है। मेरे जवाब को इस तरह देखें कि उन आवश्यकताओं से बचा जा सके।
डगलस लीडर

3
अच्छा और स्पष्ट है, लेकिन यह एक समस्या है अगर टी एक "भारी" वर्ग है।
डी जे

84

C ++ 11 संगत कंपाइलर के साथ, आप कुछ इस तरह कर सकते हैं:

template<class Derived> class MyClass {

    MyClass() {
        // Compile-time sanity check
        static_assert(std::is_base_of<BaseClass, Derived>::value, "Derived not derived from BaseClass");

        // Do other construction related stuff...
        ...
   }
}

मैं एक CYGWIN पर्यावरण के अंदर gcc 4.8.1 संकलक का उपयोग करके इसका परीक्षण कर चुका हूं - इसलिए इसे * nix वातावरण में भी काम करना चाहिए।


मेरे लिए यह इस तरह भी काम करता है: template<class TEntity> class BaseBiz { static_assert(std::is_base_of<BaseEntity, TEntity>::value, "TEntity not derived from BaseEntity");...
मैथियस डायटर वॉलनफर

1
मुझे लगता है कि यह सबसे पठनीय उत्तर है जो रनटाइम के दौरान अतिरिक्त कोड से बचा जाता है।
काइल

50

रनटाइम पर कम बेकार कोड को निष्पादित करने के लिए आप यहां देख सकते हैं: http://www.stroustrup.com/bs_faq2.html#constraints जो कुछ कक्षाएं प्रदान करता है जो संकलित समय परीक्षण कुशलतापूर्वक करते हैं, और अच्छे त्रुटि संदेश उत्पन्न करते हैं।

विशेष रूप से:

template<class T, class B> struct Derived_from {
        static void constraints(T* p) { B* pb = p; }
        Derived_from() { void(*p)(T*) = constraints; }
};

template<class T> void function() {
    Derived_from<T,Baseclass>();
}

2
मेरे लिए, यह सबसे अच्छा और सबसे दिलचस्प जवाब है। सभी प्रकार की बाधाओं के बारे में अधिक पढ़ने के लिए स्ट्रॉस्ट्रुप के अक्सर पूछे जाने वाले प्रश्नों की जांच करना सुनिश्चित करें, जो आप इसी तरह लागू कर सकते हैं।
जीन-फिलिप पेलेट

1
वास्तव में, यह एक उत्तर का एक नरक है! धन्यवाद। इस साइट का उल्लेख यहां किया गया है: stroustrup.com/bs_faq2.html#constraints
Jan Korous

यह एक बेहतरीन जवाब है। क्या चेतावनी से बचने के लिए कोई अच्छा तरीका है unused variable 'p'और unused variable 'pb'?
फिलिप एस।

@ फीलिपस। के (void)pb;बाद जोड़ें B* pb = p;
bit2shift

11

आपको अवधारणाओं की आवश्यकता नहीं है, लेकिन आप SFINAE का उपयोग कर सकते हैं:

template <typename T>
boost::enable_if< boost::is_base_of<Base,T>::value >::type function() {
   // This function will only be considered by the compiler if
   // T actualy derived from Base
}

ध्यान दें कि यह फ़ंक्शन को केवल तभी पूरा करेगा जब शर्त पूरी हो जाएगी, लेकिन यह एक समझदार त्रुटि प्रदान नहीं करेगा यदि शर्त पूरी नहीं हुई है।


यदि आप इस तरह से सभी कार्यों को लपेटते हैं तो क्या होगा? btw यह क्या लौटाता है?
the_drow

enable_ifकरने के लिए है कि चूक पैरामीटर एक दूसरे प्रकार लेता है void। अभिव्यक्ति enable_if< true, int >::typeप्रकार का प्रतिनिधित्व करती है int। मैं वास्तव में नहीं समझ सकता कि आपका पहला सवाल क्या है, आप जो भी चाहें, उसके लिए आप एसएफएनईईई का उपयोग कर सकते हैं, लेकिन मैं यह बिल्कुल नहीं समझता कि आप सभी कार्यों के साथ क्या करना चाहते हैं।
डेविड रॉड्रिग्ज - drieas

7

C ++ 11 के बाद से आपको Boost या की आवश्यकता नहीं है static_assert। C ++ 11 परिचय is_base_of और enable_if। C ++ 14 सुविधा प्रकार का परिचय देता है enable_if_t, लेकिन यदि आप C ++ 11 के साथ फंस गए हैं, तो आप enable_if::typeइसके बजाय बस उपयोग कर सकते हैं ।

वैकल्पिक 1

डेविड रोड्रिग्स के समाधान को इस प्रकार फिर से लिखा जा सकता है:

#include <type_traits>

using namespace std;

template <typename T>
enable_if_t<is_base_of<Base, T>::value, void> function() {
   // This function will only be considered by the compiler if
   // T actualy derived from Base
}

वैकल्पिक 2

C ++ 17 के बाद से, हमारे पास है is_base_of_v। समाधान को आगे फिर से लिखा जा सकता है:

#include <type_traits>

using namespace std;

template <typename T>
enable_if_t<is_base_of_v<Base, T>, void> function() {
   // This function will only be considered by the compiler if
   // T actualy derived from Base
}

वैकल्पिक 3

तुम भी पूरे टेम्पलेट को प्रतिबंधित कर सकते हैं। आप पूरी कक्षाओं को परिभाषित करने के लिए इस पद्धति का उपयोग कर सकते हैं। ध्यान दें कि दूसरे पैरामीटर को कैसे enable_if_tहटा दिया गया है (यह पहले शून्य पर सेट किया गया था)। इसका डिफ़ॉल्ट मान वास्तव में है void, लेकिन इससे कोई फर्क नहीं पड़ता, क्योंकि हम इसका उपयोग नहीं कर रहे हैं।

#include <type_traits>

using namespace std;

template <typename T,
          typename = enable_if_t<is_base_of_v<Base, T>>>
void function() {
   // This function will only be considered by the compiler if
   // T actualy derived from Base
}

टेम्पलेट मापदंडों के प्रलेखन से, हम देखते हैं कि typename = enable_if_t...एक खाली नाम के साथ एक टेम्पलेट पैरामीटर है। हम केवल यह सुनिश्चित करने के लिए इसका उपयोग कर रहे हैं कि एक प्रकार की परिभाषा मौजूद है। विशेष रूप से, enable_if_tपरिभाषित नहीं किया जाएगा यदि Baseइसका आधार नहीं है T

उपरोक्त तकनीक को उदाहरण के रूप में दिया गया है enable_if


यदि वैकल्पिक 3 को निम्नानुसार लिखना संभव नहीं होता तो क्या यह अच्छा नहीं होता? template <class T : Base>
मैकिनस


0

बेस क्लास में मौजूद आपके टेम्प्लेट के अंदर फ़ंक्शन को कॉल करके।

यदि आप अपने टेम्पलेट को उस प्रकार से आज़माते और त्वरित करते हैं जिसकी इस फ़ंक्शन तक पहुंच नहीं है, तो आपको एक संकलन-समय त्रुटि प्राप्त होगी।


3
इससे यह सुनिश्चित नहीं है कि T एक है BaseClass क्योंकि में घोषित सदस्यों BaseClassकी घोषणा में दोहराया जा सकता T
डैनियल ट्रेबिएन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.