2012-05-15 27 views
6

यह समस्या को समझाने के लिए एक छोटे से कठिन है, तो मैं एक उदाहरण के साथ शुरू होगा:खाका विशेषज्ञता

मुझे लगता है कि एक प्रकार और एक पूर्णांक टेम्पलेट पैरामीटर के रूप में लगातार लेता है एक वर्ग टेम्पलेट है, और मुझे लगता है कि उस टेम्पलेट का instantiations से निकाले जाते हैं बच्चे कक्षाओं की एक संख्या है:

template <class V, int i> 
struct Base 
{ 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

struct Child : public Base<int,12> 
{ 
}; 

मैं कुछ अन्य टेम्पलेट के साथ इन कक्षाओं में उपयोग करना चाहते हैं, जो विभिन्न प्रकार के लिए विशेषज्ञता है (यह टेस्ट कॉल)। चूंकि व्यवहार बेस के किसी भी तत्कालता से प्राप्त सभी वर्गों के लिए बिल्कुल समान होना चाहिए, इसलिए मैं टेस्ट से प्राप्त सभी वर्गों को संभालने वाले टेस्ट के केवल एक ही विशेषज्ञता को परिभाषित करना चाहता हूं।

मुझे पता है कि मैं बेस < वी, i > के लिए सीधे विशेषज्ञता नहीं दे सकता क्योंकि यह बाल कक्षाओं का पता नहीं लगाएगा। इसके बजाय, मेरा पहला दृष्टिकोण बूस्ट के enable_if और प्रकार लक्षण उपयोग कर रहा था:

// empty body to trigger compiler error for unsupported types 
template <class T, class Enabled = void> 
struct Test { }; 

// specialization for ints, 
// in my actual code, I have many more specializations here 
template <class Enabled> 
struct Test <int, Enabled> 
{ 
    static void test (int dst) 
    { 
     cout << "Test<int>::test(" << dst << ")" << endl; 
    } 
}; 

// this should handle all subclasses of Base, 
// but it doesn't compile 
template <class T, class V, int i> 
struct Test <T, typename enable_if <is_base_and_derived <Base <V,i>, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 

int main (int argc, char **argv) 
{ 
    Test <int>::test (23); 
    Test <Child>::test (Child()); 
    return 0; 
} 

विचार था कि विशेषज्ञता जो वी और मैं के किसी भी मनमाने ढंग से मूल्यों के साथ बेस से प्राप्त कर रहे सभी वर्गों संभाल चाहिए। यह काम नहीं करता, जीसीसी की शिकायत:

 
error: template parameters not used in partial specialization: 
error:   ‘V’ 
error:   ‘i’ 

मुझे लगता है कि समस्या यह है कि इस दृष्टिकोण वी के सभी संभव संयोजनों की कोशिश करने का संकलक की आवश्यकता होगी और अगर उनमें से किसी से मेल खाता है मैं जाँच करने के लिए है।

template <class V, int i> 
struct Base 
{ 
    typedef V VV; 
    static constexpr int ii = i; 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

इस तरह, विशेषज्ञता नहीं रह गया है और वी की जरूरत है मैं के रूप में नि: शुल्क टेम्पलेट पैरामीटर::

template <class T> 
struct Test <T, typename enable_if <is_base_and_derived <Base <typename T::VV, T::ii>, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 

और फिर अभी के लिए, मैं आधार वर्ग के लिए कुछ जोड़कर समस्या को हल काम किया यह संकलित करता है।

अब, मेरा प्रश्न है: मैं बेस क्लास को संशोधित किए बिना ऐसा कैसे कर सकता हूं? इस मामले में यह संभव था क्योंकि मैंने इसे स्वयं लिखा था, लेकिन अगर मुझे अपने टेस्ट टेम्पलेट में तीसरे पक्ष के पुस्तकालय कोड को संभालना है तो मैं क्या कर सकता हूं? क्या कोई और सुरुचिपूर्ण समाधान है?

संपादित करें: साथ ही, क्या कोई मुझे एक विस्तृत स्पष्टीकरण दे सकता है कि वास्तव में पहला दृष्टिकोण क्यों काम नहीं करता है? मेरे पास एक अजीब विचार है, लेकिन मैं उचित समझ लेना पसंद करूंगा। :-)

उत्तर

3

एक सरल उपाय बताने के लिए Base है एक और Base_base विरासत:

template <class V, int i> 
struct Base3rdparty 
{ 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

template <class V, int i> 
struct Base 
: public Base3rdparty<V, i> 
{ 
    typedef V VV; 
    static constexpr int ii = i; 
}; 

template <class T> 
struct Test <T, typename enable_if <is_base_and_derived <Base <typename T::VV, T::ii>, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 
+0

बेसबीज़ टिप के लिए धन्यवाद, जो मेरा वर्तमान कोड थोड़ा और अधिक पठनीय बनाता है। हालांकि, तीसरे पक्ष के पुस्तकालयों के लिए, यह काम नहीं करेगा, कम से कम नहीं यदि बच्चे वर्ग पुस्तकालय से संबंधित हैं। –

+1

@ बेंजामिनस्चग: शायद [यह] (http://stackoverflow.com/a/6398983/1324131) संपादित प्रश्न में आपके नए प्रश्न का उत्तर देता है। – user2k5

+0

धन्यवाद, यह बताता है कि पहला दृष्टिकोण क्यों काम नहीं करता था।ऐसा लगता है कि मेरी आंत महसूस सही थी। –

0

:

struct Base_base 
{}; 

template <class V, int i> 
struct Base 
: public Base_base 
{ 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

template <class T> 
struct Test <T, typename enable_if <is_base_and_derived <Base_base, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 

[संपादित] एक 3 पार्टी कोड में, आप एक चाल की तरह उपयोग कर सकते हैं फ़ंक्शन ओवरलोडिंग और decltype का उपयोग करें:

// Never defined: 
template<typename T> std::false_type is_Base(T&); 
template<class V, int I> std::true_type is_Base(Base<V,I>&); 

template<typename IsBase, typename T> struct TestHelper; 
template<typename T> struct TestHelper<std::true_type, T> 
{ 
    static void test(const T& dst) { dst.doSomething(); } 
}; 
template<> struct TestHelper<std::false_type, int> 
{ 
    static void test(int dst) 
    { std::cout << "Test<int>::test(" << dst << ")" << std::endl; } 
}; 
// ... 

template<typename T> struct Test 
{ 
    static void test(const T& dst) 
    { TestHelper<decltype(is_Base(std::declval<T&>())), T>::test(dst); } 
} 
संबंधित मुद्दे