2011-02-27 19 views
6

मैं आमतौर पर अपने वर्गों और टेम्पलेट्स की घोषणा करता हूं, और फिर उनके तरीकों को परिभाषित करता हूं (बिल्कुल उसी हेडर फ़ाइल में)। मुझे बस उस तरह से पढ़ने में आसान लगता है। खैर, मैं ऐसे मामले में आया हूं जहां मैं कक्षा के बाहर की परिभाषा में उपयोग करने के लिए एक कामकाजी प्रकार हस्ताक्षर नहीं समझ सकता। यहाँ मैं क्या कर रहा हूं की एक सरल उदाहरण है, कि समस्या दिखाता है:एक सक्षम_ifed टेम्पलेट टेम्पलेट कन्स्ट्रक्टर का प्रकार हस्ताक्षर?

template <class T> 
struct Foo 
    { 
    Foo(T a, T b); 

    template 
     < class Iterator 
     , enable_if< is_iterator<Iterator> > 
     > 
    Foo 
     (Iterator first 
     , Iterator last 
    ); 
    }; 

template <class T> 
Foo<T>::Foo(T a, T b) 
{ ... } 

template <class T> 
template 
    < class U 
    , WHAT_GOES_HERE? 
    > 
Foo<T>::Foo(U f, U l) 
{ ... } 

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

संपादित करें: मैं, < enable_if उल्लेख करना चाहिए कि मैं सिर्फ नहीं कर सकते फिर से उपयोग ...> परिभाषा क्योंकि enable_if < ...> अपने प्रकार है, जो आप में ऐसा नहीं कर सकते के लिए एक डिफ़ॉल्ट मान प्रदान करती है एक परिभाषा जो एक घोषणा भी नहीं है।

+1

आप वास्तव में इस के लिए SFINAE की ज़रूरत है? यदि आप दूसरे कन्स्ट्रक्टर को 'टेम्पलेट फू (यू पहले, यू अंतिम) के रूप में घोषित करते हैं;', पहले कन्स्ट्रक्टर का चयन तब भी किया जाएगा जब कॉलर टाइप की दो ऑब्जेक्ट्स 'टी' पास करता है। –

+0

टाइप टी आमतौर पर एक अंकगणितीय प्रकार होता है, और जब मैं टी हस्ताक्षरित होता है और इसके विपरीत, और टेम्पलेट कन्स्ट्रक्टर को कॉल नहीं किया जाता है (जो मैं सक्षम_आईफ़ का उपयोग करने से पहले हो रहा था) – swestrup

+0

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

उत्तर

3

मैं ऐसा नहीं करता। यहां उन परिवर्तनों मैं होगा है:

template <class T> 
struct Foo 
    { 
    Foo(T a, T b); 

    template 
     < class Iterator 
     > 
    Foo 
     (Iterator first 
     , Iterator last 
     , typename enable_if<is_iterator<Iterator> >::type* = 0 
    ); 
    }; 

template <class T> 
Foo<T>::Foo(T a, T b) 
{ ... } 

template <class T> 
template 
    < class U 
    > 
Foo<T>::Foo(U f, U l, typename enable_if< is_iterator<U> >::type*) 
{ ... } 

यह सीधे enable_if के लिए दस्तावेज़ से बाहर है।

+0

दिलचस्प है, मैंने कई बार enable_if दस्तावेज़ों के माध्यम से पढ़ा है, और यह कभी भी एक कन्स्ट्रक्टर के रूप में एक नेस्टेड फ़ंक्शन के रूप में इलाज करने के लिए नहीं हुआ ... यदि यह काम करता है, तो यह निश्चित रूप से स्वीकार्य है। – swestrup

+0

देखें http://www.boost.org/doc/libs/1_46_0/libs/utility/enable_if।एचटीएमएल सेक्शन 3: "कन्स्ट्रक्टर और विनाशकों के पास रिटर्न टाइप नहीं है; एक अतिरिक्त तर्क ही एकमात्र विकल्प है।" –

+0

धन्यवाद! वास्तव में यह चाल चल रही थी। क्या इसका मतलब यह है कि मैं पहले जो कोशिश कर रहा था वह असंभव था, उसमें एक प्रकार का हस्ताक्षर देने का कोई तरीका नहीं है जो सी ++ को संतुष्ट करेगा? – swestrup

2

क्या आप इसे पूरा करने की कोशिश कर रहे हैं? [मेरे पास is_iterator टाइप विशेषता नहीं है, इसलिए मैंने सी ++ 0x प्रकार के गुणों और उपयोगिता पुस्तकालयों का उपयोग करके अपना उदाहरण दोबारा बनाया है। इसे TR1 और बूस्ट लाइब्रेरीज़ के साथ उसी तरह काम करना चाहिए।]

#include <utility> 
#include <type_traits> 

template <typename T> 
struct S 
{ 
    // Constructor (1) 
    S(T, T); 

    // Constructor (2) 
    template <typename U> 
    S(U, U, typename std::enable_if<std::is_integral<U>::value>::type* = 0); 
}; 

template <typename T> 
S<T>::S(T, T) 
{ } 

template <typename T> 
template <typename U> 
S<T>::S(U, U, typename std::enable_if<std::is_integral<U>::value>::type*) 
{ } 

int main() 
{ 
    S<double> a(1.0, 2.0); // uses (1) 
    S<double> b(1, 2);  // uses (2) 
} 
+0

हाँ, यह कम या ज्यादा सही है। मुझे अपना खुद का_इटरेटर लिखना पड़ा। मुझे नहीं पता कि यह बढ़ावा देने में मानक क्यों नहीं है। – swestrup

1

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

template<class Iterator> 
Foo 
    (Iterator first 
    , typename enable_if<is_iterator<Iterator>, Iterator>::type last 
); 
1
template <class T> 
struct Foo 
    { 
    Foo(T a, T b); 

    template <class Iterator 
     ,  class = typename std::enable_if 
         <is_iterator<Iterator>::value> 
         ::type 
     > 
    Foo 
     (Iterator first 
     , Iterator last 
    ); 
    }; 

template <class T> 
Foo<T>::Foo(T a, T b) 
{ } 

template <class T> 
template 
    < class U 
    , class > 
Foo<T>::Foo(U f, U l) 
{ } 
+0

मैंने कोशिश की है, और यह काम नहीं करता है। मुझे एक संदेश मिलता है कि परिभाषा मेरे टेम्पलेट में किसी भी घोषणा से मेल नहीं खाती है। हालांकि, मुझे कहना होगा, यह ** ** काम करना चाहिए! – swestrup

+0

मैंने इसे जी ++ - 4.4 और क्लैंग के साथ परीक्षण किया। हालांकि मेरे पास मेरे परीक्षणों के लिए -std = C++ 0x था और अब मैं दोबारा जांच करता हूं, मुझे लगता है कि यह आवश्यक है। इसके बिना क्लैंग इस चेतावनी देता है: चेतावनी: फ़ंक्शन टेम्पलेट के लिए डिफ़ॉल्ट टेम्पलेट तर्क एक C++ 0x एक्सटेंशन हैं [-WC++ 0x-एक्सटेंशन] , कक्षा = टाइपनाम std :: enable_if –

+0

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

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