2012-01-03 22 views
9

मैं बेस क्लास निर्धारित करने के लिए टेम्पलेट मेटा-प्रोग्रामिंग का उपयोग करने की कोशिश कर रहा हूं। क्या प्रत्येक व्युत्पन्न कक्षा के लिए स्पष्ट रूप से विशेषज्ञता के बिना बेस क्लास स्वचालित रूप से प्राप्त करने का कोई तरीका है?क्या बेस क्लास का प्रकार टेम्पलेट प्रकार से स्वचालित रूप से प्राप्त किया जा सकता है?

class foo { public: char * Name() { return "foo"; }; }; 
class bar : public foo { public: char * Name() { return "bar"; }; }; 

template< typename T > struct ClassInfo { typedef T Base; }; 
template<> struct ClassInfo<bar> { typedef foo Base; }; 

int main() 
{ 
    ClassInfo<foo>::Base A; 
    ClassInfo<bar>::Base B; 

    std::cout << A.Name(); //foo 
    std::cout << B.Name(); //foo 
} 

के लिए अभी किसी भी स्वत: विधि और पहले घोषित आधार का चयन करने की आवश्यकता होगी निजी अड्डों के लिए विफल हो जाएगा।

+1

उपयोग [ 'std :: is_base_of '] (http://stackoverflow.com/questions/2910979/how-is-base-of-works) – iammilind

+3

@iammilind: यह केवल परीक्षण के लिए है, तो एक वर्ग का आधार है दूसरे के लिए, आपको पहले से ही परीक्षण करने के लिए बेस क्लास को जानना होगा। – Xeo

+4

आपको इसके लिए क्या चाहिए? मुझे नहीं लगता कि यह संभव है, लेकिन शायद वास्तविक समस्या को हल करने के लिए अलग-अलग दृष्टिकोण हैं। –

उत्तर

5

मेरे समाधान वास्तव में स्वचालित नहीं हैं, लेकिन सबसे अच्छा मैं सोच सकता हूं।

घुसपैठ सी ++ 03 समाधान:

class B {}; 

class A : public B 
{ 
public: 
    typedef B Base; 
}; 

गैर दखल सी ++ 03 समाधान:

class B {}; 

class A : public B {}; 

template<class T> 
struct TypeInfo; 

template<> 
struct TypeInfo<A> 
{ 
    typedef B Base; 
}; 
+0

जो कि मैं भी साथ आया था बिल्कुल काफी है। मैं उम्मीद कर रहा था कि कुछ कट्टर टेम्पलेट चाल थीं कि मुझे पता नहीं था कि इसे निकाला जा सकता है .. मैं शायद पूर्ण निर्माण नियंत्रण वाले कक्षाओं के लिए घुसपैठ का फॉर्म और अर्ध-विदेशी वर्गों के लिए गैर-घुसपैठ का उपयोग करूंगा - धन्यवाद – VonRiphenburger

5

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

आधार वर्ग में एक सार्वजनिक typedef अक्सर खुजली आप खरोंच है की जरूरत हो सकती है खरोंच और स्पष्ट:

class foo { public: typedef foo name_making_type; ... }; 

int main() { 
    Foo::name_making_type a; 
    Bar::name_making_type b; 
} 
+0

अच्छा एक। मैं इसे लिखने वाला था। – iammilind

+0

यह बहुत हल्के वजन सी ++ प्रतिबिंब प्रणाली के लिए है। "क्लाइंट" कोड सामान्य वर्ग क्रमशः और मूल वर्ग के न्यूनतम संभव विरूपण और/या जोड़ों के साथ संदेश भेजता है। – VonRiphenburger

15

यह साथ सी ++ 11 और decltype संभव है। इसके लिए, हम इसका फायदा उठाएंगे कि जब सदस्य को बेस क्लास से विरासत में प्राप्त किया जाता है तो एक पॉइंटर-टू-सदस्य व्युत्पन्न वर्ग में सूचक नहीं होता है।

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

struct base{ 
    void f(){} 
}; 
struct derived : base{}; 

&derived::f के प्रकार void (base::*)(), नहीं void (derived::*)() हो जाएगा। यह पहले से ही सी ++ 03 में सच था, लेकिन वास्तव में इसे निर्दिष्ट किए बिना बेस क्लास प्रकार प्राप्त करना असंभव था। decltype के साथ, यह आसान है और केवल इस छोटे से समारोह की जरूरत है:

// unimplemented to make sure it's only used 
// in unevaluated contexts (sizeof, decltype, alignof) 
template<class T, class U> 
T base_of(U T::*); 

उपयोग:

#include <iostream> 

// unimplemented to make sure it's only used 
// in unevaluated contexts (sizeof, decltype, alignof) 
template<class T, class R> 
T base_of(R T::*); 

struct base{ 
    void f(){} 
    void name(){ std::cout << "base::name()\n"; } 
}; 
struct derived : base{ 
    void name(){ std::cout << "derived::name()\n"; } 
}; 

struct not_deducible : base{ 
    void f(){} 
    void name(){ std::cout << "not_deducible::name()\n"; } 
}; 

int main(){ 
    decltype(base_of(&derived::f)) a; 
    decltype(base_of(&base::f)) b; 
    decltype(base_of(&not_deducible::f)) c; 
    a.name(); 
    b.name(); 
    c.name(); 
} 

आउटपुट:

base::name() 
base::name() 
not_deducible::name() 

पिछले उदाहरण दिखाता है के रूप में, आप एक सदस्य उपयोग करने की आवश्यकता है कि वास्तव में उस बेस क्लास का विरासत सदस्य है जिसमें आप रुचि रखते हैं।

फिर से अधिक खामियां हैं, हालांकि: सदस्य भी स्पष्ट होना एक आधार वर्ग के सदस्य की पहचान करनी चाहिए:

struct base2{ void f(){} }; 

struct not_deducible2 : base, base2{}; 

int main(){ 
    decltype(base_of(&not_deducible2::f)) x; // error: 'f' is ambiguous 
} 

यही कारण है, सबसे अच्छा आप हालांकि प्राप्त कर सकते हैं संकलक समर्थन के बिना।

+0

धन्यवाद ज़ीओ, यह एक बहुत ही रोचक आंशिक समाधान है और जो कुछ मैं ढूंढ रहा हूं उसके समान ही है। मैं इसे और देखेंगे – VonRiphenburger

2

बेस क्लास के साथ क्या है? क्या आप एक .NET या जावा प्रोग्रामर हैं?

सी ++ एकाधिक विरासत का समर्थन करता है, और इसमें वैश्विक आम आधार वर्ग भी नहीं है। तो एक सी ++ प्रकार में शून्य, एक, या कई आधार वर्ग हो सकते हैं। निश्चित लेख का उपयोग इसलिए contraindicated है।

बेस क्लास का कोई मतलब नहीं है, इसे खोजने का कोई तरीका नहीं है।

+0

मजबूत दक्षता कारण हैं जावा केवल एक बेस क्लास की अनुमति देता है और मैं उसी कारण से अपने सी ++ कोड को पुन: व्यवस्थित करता हूं - इसलिए यह एक उपयोगी सबसेट – VonRiphenburger

+0

@ वोनरिफेनबर्गर के रूप में समझ में आता है: फिर सवाल को राज्य की आवश्यकता है : "एक वर्ग के लिए बिल्कुल एक तत्काल बेस क्लास के लिए, सार्वजनिक रूप से और गैर-वर्चुअल रूप से विरासत में मिला, क्या बेस क्लास प्रकार को खोजना संभव है?" और यह अभी भी स्पष्ट नहीं है कि आप तत्काल बेस क्लास या किसी अन्य पूर्वजों की तलाश में हैं या नहीं। और जावा केवल एक बेस क्लास को दक्षता के साथ कुछ भी करने की इजाजत नहीं देता है, और भाषा डिजाइन के साथ बहुत कुछ करने के लिए कोडर को "ओओपी के लिए एक सही मार्ग" को मजबूर करने के लिए मजबूर किया जाता है। मुक्त कार्यों की कमी के लिए डितो। –

+0

मैंने पहले घोषित आधार के लिए कहा था। यह स्पष्ट प्रतीत होता था कि तत्काल आधार मांगा जा रहा था, यादृच्छिक पूर्वजों नहीं, लेकिन उस बिंदु को और स्पष्ट रूप से बताया जा सकता था।उम, आपके इनपुट के लिए धन्यवाद, बेन। – VonRiphenburger

0

मैं इसी तरह की समस्याओं के लिए एक पोर्टेबल संकल्प महीने के लिए के लिए देख रहा हूँ। लेकिन मुझे अभी तक यह नहीं मिला।

जी ++ में __bases और __direct_bases है। आप उन्हें एक प्रकार की सूची में लपेट सकते हैं और उसके किसी भी तत्व को एक्सेस कर सकते हैं, उदा। एक std::tuplestd::tuple_element के साथ। उपयोग के लिए libstdc++'s <tr2/type_traits> देखें।

हालांकि, यह पोर्टेबल नहीं है। Clang++ currently has no such intrinsics.

1
के साथ सी ++ 11

, आप हमेशा एक base_t सदस्य है, जब अपने वर्ग एक ही माता पिता से विरासत एक घुसपैठ विधि बना सकते हैं:

template<class base_type> 
struct labeled_base : public base_type 
{ 
    using base_t = base_type; // The original parent type 
    using base::base; // Inherit constructors 

protected: 
    using base = labeled_base; // The real parent type 
}; 

struct A { virtual void f() {} }; 

struct my_class : labeled_base<A> 
{ 
    my_class() : parent_t(required_params) {} 

    void f() override 
    { 
     // do_something_prefix(); 
     base_t::f(); 
     // do_something_postfix(); 
    } 
}; 
उस वर्ग के साथ

, आप हमेशा एक parent_t होगा उपनाम, अभिभावक रचनाकारों को कॉल करने के लिए जैसे कि यह base कन्स्ट्रक्टर (संभवतः) छोटे नाम के साथ, और base_t उपनाम थे, ताकि आपकी कक्षा को बेस क्लास प्रकार के नाम से अवगत कराया जा सके, यदि यह लंबा या भारी रूप से टेम्पलेट हो।

parent_t उपनाम जनता को इसका खुलासा करने के लिए सुरक्षित नहीं है। यदि आप नहीं चाहते हैं कि base_t उपनाम सार्वजनिक है, तो आप हमेशा labeled_baseprotected या private के रूप में प्राप्त कर सकते हैं, labeled_base कक्षा परिभाषा को बदलने की कोई आवश्यकता नहीं है।

उस आधार में 0 रनटाइम या स्पेस ओवरहेड होना चाहिए क्योंकि इसकी विधियां इनलाइन हैं, कुछ भी नहीं करें, और उनके पास कोई विशेष गुण नहीं है।

संबंधित मुद्दे