C ++ टेम्प्लेट जो केवल कुछ प्रकारों को स्वीकार करते हैं


158

जावा में आप जेनेरिक क्लास को परिभाषित कर सकते हैं जो केवल उन प्रकारों को स्वीकार करता है जो आपकी पसंद के वर्ग का विस्तार करते हैं, जैसे:

public class ObservableList<T extends List> {
  ...
}

यह "extends" कीवर्ड का उपयोग करके किया जाता है।

क्या C ++ में इस कीवर्ड के बराबर कुछ सरल है?


पहले से ही काफी पुराना सवाल है ... मुझे लगता है कि यहां (उत्तरों से भी) क्या गायब है कि जावा जेनेरिक वास्तव में C ++ में टेम्पलेट्स के बराबर हैं। समानताएं हैं, लेकिन imho किसी को सीधे सी ++ के लिए एक जावा समाधान का अनुवाद करने के साथ सावधान रहना चाहिए ताकि यह महसूस किया जा सके कि वे शायद विभिन्न प्रकार की समस्याओं के लिए बने हैं;)
idclev 463035818

जवाबों:


104

मेरा सुझाव है कि बूस्ट टाइप ट्रैक्ट्स लाइब्रेरी से कॉन्सर्ट में बूस्ट के स्टैटिक एस्टर फ़ीचर का उपयोग करें is_base_of:

template<typename T>
class ObservableList {
    BOOST_STATIC_ASSERT((is_base_of<List, T>::value)); //Yes, the double parentheses are needed, otherwise the comma will be seen as macro argument separator
    ...
};

कुछ अन्य, सरल मामलों में, आप केवल एक वैश्विक टेम्पलेट को आगे-घोषित कर सकते हैं, लेकिन केवल मान्य प्रकारों के लिए इसे स्पष्ट रूप से या आंशिक रूप से परिभाषित करते हैं:

template<typename T> class my_template;     // Declare, but don't define

// int is a valid type
template<> class my_template<int> {
    ...
};

// All pointer types are valid
template<typename T> class my_template<T*> {
    ...
};

// All other types are invalid, and will cause linker error messages.

[माइनर EDIT 6/12/2013: घोषित-लेकिन-नहीं-परिभाषित टेम्पलेट का उपयोग करने से लिंकर में परिणाम होगा , संकलक नहीं, त्रुटि संदेश।]


स्थैतिक जोर के रूप में अच्छी तरह से कर रहे हैं। :)
मैक्बेर्डी

5
@ जॉन: मुझे डर है कि विशेषज्ञता केवल myBaseTypeबिल्कुल मेल खाएगी । बूस्ट को खारिज करने से पहले, आपको पता होना चाहिए कि इसका अधिकांश हेडर-ओनली टेम्प्लेट कोड है - इसलिए आपके द्वारा उपयोग नहीं की जाने वाली चीजों के लिए रनटाइम पर कोई मेमोरी या समय लागत नहीं है। इसके अलावा, आप जिन विशेष चीजों का उपयोग यहां कर रहे हैं ( BOOST_STATIC_ASSERT()और is_base_of<>) केवल घोषणाओं (यानी कार्यों या चर की कोई वास्तविक परिभाषा ) का उपयोग करके लागू किया जा सकता है ताकि वे या तो स्थान या समय नहीं लेंगे।
j_random_hacker

50
C ++ 11 आ गया है। अब हम उपयोग कर सकते हैं static_assert(std::is_base_of<List, T>::value, "T must extend list")
सियुआन रेन

2
BTW, डबल कोष्ठक आवश्यक होने का कारण यह है कि BOOST_STATIC_ASSERT एक मैक्रो है और अतिरिक्त कोष्ठक प्रीप्रोसेसर को is_base_of फ़ंक्शन तर्क में 2 मैक्रो तर्क के रूप में व्याख्या करने से रोकता है।
jfritz42

1
@Andreyua: मुझे वास्तव में समझ नहीं आ रहा है कि क्या गायब है। आप एक चर घोषित करने की कोशिश कर सकते हैं my_template<int> x;या my_template<float**> y;सत्यापित कर सकते हैं कि संकलक इन्हें अनुमति देता है, और फिर एक चर घोषित my_template<char> z;करें और सत्यापित करें कि यह नहीं है।
j_random_hacker

134

यह आमतौर पर C ++ में अनुचित है, क्योंकि यहां अन्य उत्तर नोट किए गए हैं। C ++ में हम "इस वर्ग से विरासत" के अलावा अन्य बाधाओं के आधार पर सामान्य प्रकारों को परिभाषित करते हैं। यदि आप वास्तव में ऐसा करना चाहते हैं, तो C ++ 11 और ऐसा करना काफी आसान है <type_traits>:

#include <type_traits>

template<typename T>
class observable_list {
    static_assert(std::is_base_of<list, T>::value, "T must inherit from list");
    // code here..
};

यह बहुत सी अवधारणाओं को तोड़ता है जो लोग C ++ में उम्मीद करते हैं। अपने खुद के लक्षणों को परिभाषित करने जैसी चाल का उपयोग करना बेहतर है। उदाहरण के लिए, हो सकता है observable_listकि किसी भी प्रकार के कंटेनर को स्वीकार करना चाहता है जिसमें टाइपडिफ्स const_iteratorऔर ए beginऔर endसदस्य फ़ंक्शन है जो रिटर्न करता है const_iterator। यदि आप इसे उन वर्गों के लिए प्रतिबंधित करते हैं जो listतब से इनहेरिट करते हैं, तो एक उपयोगकर्ता के पास स्वयं का प्रकार होता है जो इनहेरिट नहीं करता है, listलेकिन ये सदस्य फ़ंक्शन प्रदान करता है और टाइप किए गए सेफ़ आपके उपयोग करने में असमर्थ होंगे observable_list

इस समस्या के दो समाधान हैं, उनमें से एक है किसी चीज को बाधित न करना और बतख टाइपिंग पर निर्भर रहना। इस समाधान के लिए एक बड़ी बात यह है कि इसमें भारी मात्रा में त्रुटियां हैं जो उपयोगकर्ताओं के लिए मुश्किल हो सकती हैं। एक अन्य उपाय इंटरफ़ेस आवश्यकताओं को पूरा करने के लिए प्रदान किए गए प्रकार को बाधित करने के लिए लक्षणों को परिभाषित करना है। इस समाधान के लिए बड़ी बात यह है कि इसमें अतिरिक्त लेखन शामिल है जिसे कष्टप्रद के रूप में देखा जा सकता है। हालांकि, सकारात्मक पक्ष यह है कि आप अपने स्वयं के त्रुटि संदेशों को ला लिख ​​सकेंगे static_assert

पूर्णता के लिए, ऊपर दिए गए उदाहरण का हल दिया गया है:

#include <type_traits>

template<typename...>
struct void_ {
    using type = void;
};

template<typename... Args>
using Void = typename void_<Args...>::type;

template<typename T, typename = void>
struct has_const_iterator : std::false_type {};

template<typename T>
struct has_const_iterator<T, Void<typename T::const_iterator>> : std::true_type {};

struct has_begin_end_impl {
    template<typename T, typename Begin = decltype(std::declval<const T&>().begin()),
                         typename End   = decltype(std::declval<const T&>().end())>
    static std::true_type test(int);
    template<typename...>
    static std::false_type test(...);
};

template<typename T>
struct has_begin_end : decltype(has_begin_end_impl::test<T>(0)) {};

template<typename T>
class observable_list {
    static_assert(has_const_iterator<T>::value, "Must have a const_iterator typedef");
    static_assert(has_begin_end<T>::value, "Must have begin and end member functions");
    // code here...
};

ऊपर उदाहरण में बहुत सी अवधारणाएँ दिखाई गई हैं जो C ++ 11 की विशेषताओं को दर्शाती हैं। जिज्ञासु के लिए कुछ खोज शब्द वैरेडिक टेम्प्लेट, SFINAE, अभिव्यक्ति SFINAE और टाइप लक्षण हैं।


2
मैंने कभी नहीं महसूस किया कि C ++ टेम्पलेट आज तक बतख टाइपिंग का उपयोग करते हैं। विचित्र की तरह!
एंडी

2
व्यापक नीतिगत बाधाओं को देखते हुए C ++ को C को पेश किया गया , यह सुनिश्चित नहीं है कि template<class T:list>इस तरह की आपत्तिजनक अवधारणा क्यों है। पारितोषिक के लिए धन्यवाद।
bjj

60

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

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

यदि आप अच्छे त्रुटि संदेश चाहते हैं (या यदि आप ऐसे मामलों को पकड़ना चाहते हैं जो एक संकलक त्रुटि उत्पन्न नहीं करेंगे, लेकिन फिर भी समझ में नहीं आते हैं) तो आप इस बात पर निर्भर कर सकते हैं कि आप इसे कितना जटिल बनाना चाहते हैं, या तो बूस्ट के स्थिर मुखर का उपयोग करें या बूस्ट concept_check पुस्तकालय।

अप-टू-डेट कंपाइलर के साथ आपके पास एक अंतर्निहित_इन है static_assert, जिसका उपयोग इसके बजाय किया जा सकता है।


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

@ जॉन: मुझे क्षमा करें, मैं उस का सिर या पूंछ नहीं बना सकता। कौन सा प्रकार है T, और इस कोड को कहां से कहा जाता है? कुछ संदर्भ के बिना, मुझे उस कोड स्निपेट को समझने का कोई मौका नहीं है। लेकिन मैंने जो कहा वह सच है। यदि आप toString()उस प्रकार को कॉल करने का प्रयास करते हैं जिसमें कोई toStringसदस्य फ़ंक्शन नहीं है , तो आपको एक संकलन त्रुटि मिलेगी।
११:११

@ जॉन: अगली बार, शायद आप थोड़ा कम ट्रिगर-खुश downvoting लोगों होना चाहिए जब समस्या अपने कोड में है
jalf

@ जैलफ, ओके। +1। यह एक बेहतरीन जवाब था जो इसे सर्वश्रेष्ठ बनाने की कोशिश कर रहा था। गलतफहमी के लिए क्षमा करें। मैंने सोचा था कि हम फ़ंक्शन टेम्प्लेट के लिए नहीं, कक्षाओं के लिए एक पैरामीटर के रूप में प्रकार का उपयोग करने के बारे में बात कर रहे थे, जो मुझे लगता है कि पूर्व के सदस्य हैं, लेकिन संकलक को ध्वज लगाने की आवश्यकता है।
जॉन

13

हम उपयोग कर सकते हैं std::is_base_ofऔर std::enable_if:
( static_assertहटाया जा सकता है, ऊपर वर्गों कस्टम लागू किया जा सकता या से इस्तेमाल किया बढ़ावा अगर हम संदर्भ नहीं कर सकते हैं type_traits)

#include <type_traits>
#include <list>

class Base {};
class Derived: public Base {};

#if 0   // wrapper
template <class T> class MyClass /* where T:Base */ {
private:
    static_assert(std::is_base_of<Base, T>::value, "T is not derived from Base");
    typename std::enable_if<std::is_base_of<Base, T>::value, T>::type inner;
};
#elif 0 // base class
template <class T> class MyClass: /* where T:Base */
    protected std::enable_if<std::is_base_of<Base, T>::value, T>::type {
private:
    static_assert(std::is_base_of<Base, T>::value, "T is not derived from Base");
};
#elif 1 // list-of
template <class T> class MyClass /* where T:list<Base> */ {
    static_assert(std::is_base_of<Base, typename T::value_type>::value , "T::value_type is not derived from Base");
    typedef typename std::enable_if<std::is_base_of<Base, typename T::value_type>::value, T>::type base; 
    typedef typename std::enable_if<std::is_base_of<Base, typename T::value_type>::value, T>::type::value_type value_type;

};
#endif

int main() {
#if 0   // wrapper or base-class
    MyClass<Derived> derived;
    MyClass<Base> base;
//  error:
    MyClass<int> wrong;
#elif 1 // list-of
    MyClass<std::list<Derived>> derived;
    MyClass<std::list<Base>> base;
//  error:
    MyClass<std::list<int>> wrong;
#endif
//  all of the static_asserts if not commented out
//  or "error: no type named ‘type’ in ‘struct std::enable_if<false, ...>’ pointing to:
//  1. inner
//  2. MyClass
//  3. base + value_type
}

13

जहाँ तक मुझे पता है कि यह वर्तमान में C ++ में संभव नहीं है। हालाँकि, नए C ++ 0x मानक में "कॉन्सेप्ट्स" नामक एक फीचर जोड़ने की योजना है जो आपके द्वारा देखी जा रही कार्यक्षमता प्रदान करती है। C ++ कॉन्सेप्ट के बारे में यह विकिपीडिया लेख इसे और अधिक विस्तार से बताएगा।

मुझे पता है कि यह आपकी तत्काल समस्या को ठीक नहीं करता है लेकिन कुछ C ++ कंपाइलर हैं जो नए मानक से सुविधाओं को जोड़ना शुरू कर चुके हैं, इसलिए ऐसा कंपाइलर ढूंढना संभव हो सकता है जिसने पहले से ही कॉन्सेप्ट फीचर लागू कर दिया हो।


4
दुर्भाग्य से मानक से अवधारणाओं को हटा दिया गया है।
मैकबर्डी

4
सी ++ 20 के लिए बाधाओं और अवधारणाओं को अपनाया जाना चाहिए।
पेट्र जवोरिक

यह अवधारणाओं के बिना भी संभव है, static_assertअन्य उत्तर दिखाते हुए, एसएफआईएनएई का उपयोग करके । जावा या सी #, या हास्केल (...) से आने वाले किसी के लिए शेष मुद्दा यह है कि सी ++ 20 संकलक आवश्यक अवधारणाओं के खिलाफ परिभाषा की जाँच नहीं करता है , जो जावा और सी # करते हैं।
user7610

10

मुझे लगता है कि सभी पूर्व के उत्तर पेड़ों के लिए जंगल की दृष्टि खो चुके हैं।

जावा जेनरिक टेम्प्लेट के समान नहीं हैं ; वे संकलित समय के बहुरूपता के बजाय टाइप इरेज़र का उपयोग करते हैं , जो एक गतिशील तकनीक है , जो स्थैतिक तकनीक है । यह स्पष्ट होना चाहिए कि ये दोनों बहुत अलग रणनीति अच्छी तरह से क्यों नहीं करते हैं।

एक रन टाइम का अनुकरण करने के लिए एक संकलन समय निर्माण का उपयोग करने के प्रयास के बजाय, आइए देखें कि extendsवास्तव में क्या करता है: स्टैक ओवरफ्लो और विकिपीडिया के अनुसार , उप-वर्गिंग को इंगित करने के लिए एक्सटेंड्स का उपयोग किया जाता है।

C ++ भी उपवर्ग का समर्थन करता है।

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

आइए इसे एक टाईपेडेफ़ में लपेटें, जिससे इसे उपयोग करने में आसानी हो, बल्कि पूरी कक्षा, एट वॉयला बनाने के लिए:

typedef std::list<superclass*> subclasses_of_superclass_only_list;

उदाहरण के लिए:

class Shape { };
class Triangle : public Shape { };

typedef std::list<Shape*> only_shapes_list;
only_shapes_list shapes;

shapes.push_back(new Triangle()); // Works, triangle is kind of shape
shapes.push_back(new int(30)); // Error, int's are not shapes

अब, ऐसा लगता है कि सूची एक इंटरफ़ेस है, जो एक प्रकार के संग्रह का प्रतिनिधित्व करता है। C ++ में एक इंटरफ़ेस केवल एक अमूर्त वर्ग होगा, यानी एक ऐसा वर्ग जो शुद्ध आभासी तरीकों के अलावा कुछ नहीं करता है। इस पद्धति का उपयोग करके, आप किसी भी अवधारणाओं या टेम्पलेट विशेषज्ञता के बिना, आसानी से अपने जावा उदाहरण को C ++ में लागू कर सकते हैं। यह वर्चुअल टेबल लुक अप्स के कारण जावा स्टाइल जेनेरिक की तरह धीमी गति से भी प्रदर्शन करेगा, लेकिन यह अक्सर स्वीकार्य नुकसान हो सकता है।


3
मैं उन उत्तरों का प्रशंसक नहीं हूं जो वाक्यांशों का उपयोग करते हैं जैसे "यह स्पष्ट होना चाहिए," या "हर कोई जानता है", और फिर स्पष्ट या सार्वभौमिक रूप से ज्ञात करने के लिए आगे बढ़ें। स्पष्टता संदर्भ, अनुभव और अनुभव के संदर्भ के सापेक्ष है। इस तरह के बयान स्वाभाविक तौर पर असभ्य हैं।
डी जुले

2
@DavidLively शिष्टाचार के लिए इस जवाब की आलोचना करने में लगभग दो साल बहुत देर हो चुकी है, लेकिन मैं इस विशिष्ट उदाहरण में भी आपसे असहमत हूं; मैंने समझाया कि स्पष्ट होने से पहले वे दो तकनीकें एक साथ क्यों नहीं चलतीं, यह स्पष्ट नहीं था। मैंने संदर्भ प्रदान किया, और फिर कहा कि उस संदर्भ से निष्कर्ष स्पष्ट था। यह आपके साँचे में बिल्कुल फिट नहीं है।
एलिस

इस जवाब के लेखक ने कहा कि कुछ भारी उठाने के बाद कुछ स्पष्ट था। मुझे नहीं लगता कि लेखक का कहना है कि समाधान स्पष्ट था।
ल्यूक गेहोरसम

10

एक समतुल्य जो केवल T को टाइप सूची से प्राप्त T को स्वीकार करता है, ऐसा दिखता है

template<typename T, 
         typename std::enable_if<std::is_base_of<List, T>::value>::type* = nullptr>
class ObservableList
{
    // ...
};

8

कार्यकारी सारांश: ऐसा मत करो।

j_random_hacker का उत्तर आपको बताता है कि यह कैसे करना है। हालाँकि, मैं यह भी बताना चाहूंगा कि आपको ऐसा नहीं करना चाहिए । टेम्पलेट्स का पूरा बिंदु यह है कि वे किसी भी संगत प्रकार को स्वीकार कर सकते हैं, और जावा शैली प्रकार की बाधाएं टूट जाती हैं।

जावा के प्रकार की बाधाएं एक बग है जो एक विशेषता नहीं है। वे वहाँ हैं क्योंकि जावा जेनेरिक पर टाइप मिटा देता है, इसलिए जावा यह पता नहीं लगा सकता है कि अकेले प्रकार के मापदंडों के आधार पर तरीकों को कैसे कॉल किया जाए।

दूसरी ओर C ++ में ऐसा कोई प्रतिबंध नहीं है। टेम्प्लेट पैरामीटर प्रकार उन ऑपरेशंस के साथ संगत हो सकते हैं, जिनके साथ उनका उपयोग किया जाता है। एक सामान्य आधार वर्ग होना जरूरी नहीं है। यह पायथन के "डक टाइपिंग" के समान है, लेकिन संकलन समय पर किया गया है।

टेम्प्लेट की शक्ति दिखाने वाला एक सरल उदाहरण:

// Sum a vector of some type.
// Example:
// int total = sum({1,2,3,4,5});
template <typename T>
T sum(const vector<T>& vec) {
    T total = T();
    for (const T& x : vec) {
        total += x;
    }
    return total;
}

यह योग फ़ंक्शन किसी भी प्रकार के वेक्टर को जोड़ सकता है जो सही संचालन का समर्थन करता है। यह इंटिमेंट / लॉन्ग / फ्लोट / डबल, और यूज़र डिफ़ाइंड न्यूमेरिक प्रकार दोनों के साथ काम करता है जो + = ऑपरेटर को ओवरलोड करते हैं। हेक, आप स्ट्रिंग्स में शामिल होने के लिए भी इस फ़ंक्शन का उपयोग कर सकते हैं, क्योंकि वे + = का समर्थन करते हैं।

आदिमों की कोई बॉक्सिंग / अनबॉक्सिंग आवश्यक नहीं है।

ध्यान दें कि यह T () का उपयोग करके T के नए उदाहरण भी बनाता है। यह अंतर्निहित इंटरफ़ेस का उपयोग करके C ++ में तुच्छ है, लेकिन प्रकार की बाधाओं के साथ जावा में वास्तव में संभव नहीं है।

हालांकि C ++ टेम्प्लेट में स्पष्ट प्रकार की बाधाएं नहीं होती हैं, फिर भी वे सुरक्षित हैं, और कोड के साथ संकलन नहीं करेंगे जो सही संचालन का समर्थन नहीं करता है।


2
यदि आप सुझाव दे रहे हैं कि कभी भी विशेषज्ञता वाले टेम्पलेट नहीं हैं, तो आप यह भी बता सकते हैं कि यह भाषा में क्यों है?

1
मुझे आपकी बात मिल गई है, लेकिन यदि आपका टेम्प्लेट तर्क किसी विशिष्ट प्रकार से प्राप्त किया जाना चाहिए, तो सामान्य कंपाइलर त्रुटि उल्टी की तुलना में static_assert से संदेश की व्याख्या करना आसान है।
jhoffman0x

1
हां, C ++ यहां अधिक अभिव्यंजक है, लेकिन जबकि यह आम तौर पर एक अच्छी बात है (क्योंकि हम कम के साथ अधिक व्यक्त कर सकते हैं), कभी-कभी हम जानबूझकर खुद को देने वाली शक्ति को सीमित करना चाहते हैं, निश्चितता हासिल करने के लिए कि हम पूरी तरह से एक प्रणाली को समझते हैं।
j_random_hacker

@Curg टाइप विशेषज्ञता उपयोगी है जब आप किसी ऐसी चीज का लाभ उठाने में सक्षम होना चाहते हैं जो केवल कुछ प्रकारों के लिए किया जा सकता है। उदाहरण के लिए, एक बूलियन है ~ आम तौर पर ~ एक बाइट प्रत्येक, भले ही एक बाइट कर सकते हैं ~ आम तौर पर ~ 8 बिट्स / बूलियन पकड़; एक टेम्प्लेट कलेक्शन क्लास (और std :: map does के मामले में) बूलियन के लिए विशेषज्ञ हो सकता है ताकि वह मेमोरी को संरक्षित करने के लिए डेटा को अधिक कसकर पैक कर सके।
Thecoshman

इसके अलावा, स्पष्ट करने के लिए, यह उत्तर यह नहीं कहता है कि "कभी भी टेम्पलेट को विशेषज्ञ न करें" यह कह रहा है कि उस सुविधा का उपयोग यह सीमित करने के लिए न करें कि किस प्रकार का उपयोग टेम्पलेट के साथ किया जा सकता है।
Thecoshman

6

यह सादे C ++ में संभव नहीं है, लेकिन आप Boost's BCCL का उपयोग करते हुए, Concept Checking के माध्यम से संकलन-समय पर टेम्पलेट मापदंडों को सत्यापित कर सकते हैं ।

C ++ 20 के अनुसार, अवधारणाएं भाषा की आधिकारिक विशेषता बन रही हैं।


2
खैर, यह है संभव है, लेकिन अवधारणा परीक्षण को अब भी एक अच्छा विचार है। :)
j_random_hacker

मेरा वास्तव में मतलब था कि यह "सादे" सी ++ में संभव नहीं था। ;)
मैकबर्डी

5
class Base
{
    struct FooSecurity{};
};

template<class Type>
class Foo
{
    typename Type::FooSecurity If_You_Are_Reading_This_You_Tried_To_Create_An_Instance_Of_Foo_For_An_Invalid_Type;
};

सुनिश्चित करें कि व्युत्पन्न वर्ग FooSecurity संरचना प्राप्त करते हैं और संकलक सभी सही स्थानों पर परेशान हो जाएगा।


@Zehelvion Type::FooSecurityका उपयोग टेम्पलेट क्लास में किया जाता है। यदि वर्ग, टेम्पलेट तर्क में पारित किया गया है, तो इसका FooSecurityउपयोग करने का प्रयास करने से त्रुटि नहीं होती है। यह निश्चित है कि यदि क्लास टेम्प्लेट तर्क में पारित हो गया है तो FooSecurity नहीं है, जो इससे व्युत्पन्न नहीं है Base
जिंजरप्लस २५

2

C ++ 20 अवधारणा उपयोग

https://en.cppreference.com/w/cpp/language/constraints cppreference एक स्पष्ट अवधारणा उदाहरण के रूप में उत्तराधिकार उपयोग के मामले दे रहा है:

template <class T, class U>
concept Derived = std::is_base_of<U, T>::value;
 
template<Derived<Base> T>
void f(T);  // T is constrained by Derived<T, Base>

कई आधारों के लिए मैं वाक्य-विन्यास का अनुमान लगा रहा हूँ:

template <class T, class U, class V>
concept Derived = std::is_base_of<U, T>::value || std::is_base_of<V, T>::value;
 
template<Derived<Base1, Base2> T>
void f(T);

GCC 10 ने इसे लागू किया प्रतीत होता है: https://gcc.gnu.org/gcc-10/changes.html और आप इसे Ubuntu 20.04 पर PPA के रूप में प्राप्त कर सकते हैं । https://godbolt.org/ मेरा स्थानीय जीसीसी 10.1 conceptअभी तक नहीं पहचाना गया, इसलिए सुनिश्चित नहीं है कि क्या चल रहा है।


1

क्या C ++ में इस कीवर्ड के बराबर कुछ सरल है?

नहीं।

जो आप पूरा करने की कोशिश कर रहे हैं, उसके आधार पर पर्याप्त (या और भी बेहतर) विकल्प हो सकते हैं।

मैंने कुछ एसटीएल कोड के माध्यम से देखा है (लिनक्स पर, मुझे लगता है कि यह एसजीआई के कार्यान्वयन से निकला है)। इसमें "अवधारणा अभिकथन" है; उदाहरण के लिए, यदि आपको एक प्रकार की आवश्यकता होती है जो समझता है *xऔर ++x, अवधारणा अभिकथन में वह कोड होगा जो कुछ भी नहीं फ़ंक्शन (या समान) में है। इसके लिए कुछ ओवरहेड की आवश्यकता होती है, इसलिए इसे मैक्रो में रखने के लिए स्मार्ट हो सकता है जिसकी परिभाषा निर्भर करती है #ifdef debug

यदि उपवर्ग संबंध वास्तव में आप के बारे में जानना चाहते हैं, तो आप कंस्ट्रक्टर में जोर दे सकते हैं T instanceof list(सिवाय इसके कि इसे "C ++ में" अलग तरीके से लिखा गया है)। इस तरह, आप कंपाइलर से अपने तरीके का परीक्षण करवा सकते हैं ताकि वह आपके लिए जाँच न कर सके।


1

इस तरह के चेक के लिए कोई कीवर्ड नहीं है, लेकिन आप इसमें कुछ कोड डाल सकते हैं, जो कम से कम एक क्रमबद्ध तरीके से विफल हो जाएगा:

(1) यदि आप एक फंक्शन टेम्प्लेट चाहते हैं कि केवल एक निश्चित आधार वर्ग X के मापदंडों को स्वीकार करें, तो इसे अपने फ़ंक्शन में X संदर्भ में असाइन करें। (2) यदि आप कार्यों को स्वीकार करना चाहते हैं, लेकिन आदिम या इसके विपरीत नहीं, या आप अन्य तरीकों से कक्षाओं को फ़िल्टर करना चाहते हैं, तो अपने फ़ंक्शन के भीतर एक (खाली) टेम्पलेट सहायक फ़ंक्शन को कॉल करें जो केवल उन वर्गों के लिए परिभाषित है जिन्हें आप स्वीकार करना चाहते हैं।

आप पूरे वर्ग पर इन प्रकार की जाँचों को लागू करने के लिए एक वर्ग के सदस्य कार्यों में (१) और (२) का उपयोग कर सकते हैं।

आप शायद अपने दर्द को कम करने के लिए इसे कुछ स्मार्ट मैक्रो में डाल सकते हैं। :)


-2

ठीक है, आप अपने टेम्पलेट को कुछ इस तरह से बना सकते हैं:

template<typename T>
class ObservableList {
  std::list<T> contained_data;
};

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

मेरे ज्ञान के सर्वश्रेष्ठ के लिए एक निर्माण जो कि स्टेटमेंट जावा स्टेटमेंट को उसकी पूर्ण सीमा तक प्रतिबिंबित करेगा, वर्तमान मानक में मौजूद नहीं है।

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

C ++ 11 में, अवधारणाओं की शुरूआत को आसान बनाना चाहिए, लेकिन मुझे नहीं लगता कि यह वही करेगा जो आप चाहते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.