2012-10-12 21 views
26

के लिए SFINAE का उपयोग कर रहा इन घोषणाओंटेम्पलेट वर्ग विशेषज्ञता

template<typename T> class User; 
template<typename T> class Data; 

है और T = Data<some_type>के लिए User<> लागू करना चाहते हैं और किसी भी वर्ग Data<some_type> से ली गई लेकिन यह भी कहीं परिभाषित अन्य विशेषज्ञताओं के लिए अनुमति देने लगता है।

मैं पहले से ही वर्ग टेम्पलेट User<> की घोषणा नहीं था, तो मैं कर सकता बस

template<typename T, 
     typename A= typename std::enable_if<is_Data<T>::value>::type> 
class User { /*...*/ }; 

जहां

template<template<typename> data>> struct is_Data 
{ static const bool value = /* some magic here (not the question) */; }; 

बहरहाल, यह दो टेम्प्लेट पैरामीटर और इस प्रकार पिछले के साथ हुई मुठभेड़ है घोषणा, जहां User<> केवल एक टेम्पलेट पैरामीटर के साथ घोषित किया गया है। क्या मुझे कुछ और करना है?

(ध्यान दें

template<typename T, 
     typename A= typename std::enable_if<is_Data<T>::value>::type> 
class User<T> { /*...*/ }; 

काम नहीं करता है (डिफ़ॉल्ट टेम्पलेट तर्क आंशिक विशेषज्ञताओं), में नहीं किया जा सकता है और न ही के रूप में यह प्रकार व्युत्पन्न की अनुमति नहीं है

template<typename T> class User<Data<T>> { /*...*/ }; 

करता है Data<> से, न तो

template<typename T> 
class User<typename std::enable_if<is_Data<T>::value,T>::type> 
{ /*...*/ }; 

के बाद से टेम्पलेट पैरामीटर T आंशिक विशेषज्ञता में नहीं किया जाता है।)

+2

SFINAE काम करता है के साथ डिफ़ॉल्ट तर्क की जगह ** कर सकते हैं ** टेम्पलेट विशेषज्ञता चुनने के लिए आवेदन किया जाए, http://en.cppreference.com/w/cpp/types/enable_if – Walter

+0

देखें तो यह कर सकता है! मैंने कुछ सीखा –

+0

मुझे नहीं लगता कि मैं समझता हूं कि 'static_assert' संस्करण क्यों काम नहीं करेगा। विस्तृत करने के लिए परवाह? – jrok

उत्तर

6

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

समाधान उपनाम टेम्पलेट्स की एक विशेषता पर निर्भर करता है जो मानक कामकाजी ड्राफ्ट में हाल ही में निर्दिष्ट किया गया था सी ++ 14 के अंतिम संस्करण के बाद, लेकिन कुछ समय के लिए कार्यान्वयन द्वारा समर्थित किया गया है। ड्राफ्ट N4527 [14.5.7p3] में प्रासंगिक शब्द है:

हालांकि, यदि टेम्पलेट-आईडी निर्भर है, तो बाद में टेम्पलेट तर्क प्रतिस्थापन टेम्पलेट-आईडी पर लागू होता है। [उदाहरण:

template<typename...> using void_t = void; 
template<typename T> void_t<typename T::foo> f(); 
f<int>(); // error, int does not have a nested type foo 

अंत उदाहरण]

यहां एक संपूर्ण उदाहरण इस विचार को लागू करने के लिए:

#include <iostream> 
#include <type_traits> 
#include <utility> 

template<typename> struct User { static void f() { std::cout << "primary\n"; } }; 

template<typename> struct Data { }; 
template<typename T, typename U> struct Derived1 : Data<T*> { }; 
template<typename> struct Derived2 : Data<double> { }; 
struct DD : Data<int> { }; 

template<typename T> void take_data(Data<T>&&); 

template<typename T, typename = decltype(take_data(std::declval<T>()))> 
using enable_if_data = T; 

template<template<typename...> class TT, typename... Ts> 
struct User<enable_if_data<TT<Ts...>>> 
{ 
    static void f() { std::cout << "partial specialization for Data\n"; } 
}; 

template<typename> struct Other { }; 
template<typename T> struct User<Other<T>> 
{ 
    static void f() { std::cout << "partial specialization for Other\n"; } 
}; 

int main() 
{ 
    User<int>::f(); 
    User<Data<int>>::f(); 
    User<Derived1<int, long>>::f(); 
    User<Derived2<char>>::f(); 
    User<DD>::f(); 
    User<Other<int>>::f(); 
} 

चल रहा है यह प्रिंट:

primary 
partial specialization for Data 
partial specialization for Data 
partial specialization for Data 
primary 
partial specialization for Other 

आप देख सकते हैं , एक झुर्रियां है: आंशिक विशेषज्ञता का चयन नहीं किया जाता है DD, और यह नहीं हो सकता है, जिस तरह से हमने इसे घोषित किया है। तो, क्यों करते हैं हम सिर्फ

template<typename T> struct User<enable_if_data<T>> 

कहना और यह DD मिलान करने के लिए और साथ ही अनुमति नहीं? यह वास्तव में जीसीसी में काम करता है, लेकिन सही ढंग से (, [p8.3] भविष्य में गायब हो सकता है के रूप में यह अनावश्यक है - CWG 2033) बजना और MSVC द्वारा अस्वीकार कर दिया गया है की वजह से [14.5.5p8.3, 8.4]:

  • विशेषज्ञता की तर्क सूची प्राथमिक टेम्पलेट की निहित तर्क सूची के समान नहीं होगी।
  • विशेषज्ञता प्राथमिक टेम्पलेट (14.5.5.2) से अधिक विशिष्ट होगी।

User<enable_if_data<T>>User<T> (कि डिफ़ॉल्ट तर्क है, जो अलग रूप में ऊपर पहली बोली से समझाया नियंत्रित किया जाता है, में सापेक्ष प्रतिस्थापन), इस प्रकार आंशिक विशेषज्ञता के किसी अमान्य फ़ॉर्म के बराबर है।दुर्भाग्य से, DD जैसे मिलान करने वाली चीजों को सामान्य रूप से T के आंशिक विशेषज्ञता तर्क की आवश्यकता होगी - इसमें कोई अन्य रूप नहीं है और अभी भी हर मामले से मेल खाता है। इसलिए, मुझे डर है कि हम निश्चित रूप से कह सकते हैं कि इस हिस्से को दिए गए बाधाओं के भीतर हल नहीं किया जा सकता है। जब तक Data<T> से व्युत्पन्न वर्ग खुद को कर रहे हैं टेम्पलेट विशेषज्ञताओं, (वहाँ Core issue 1980, जो टेम्पलेट उपनाम के उपयोग के संबंध में कुछ संभावित भविष्य के नियमों पर संकेत है, लेकिन मुझे शक है वे हमारे मामले वैध बना देंगे।)

आगे उन्हें बाधित उपर्युक्त तकनीक का उपयोग करके काम करेगा, इसलिए उम्मीद है कि यह आपके लिए कुछ उपयोग होगा।


संकलक समर्थन (इस मैं क्या परीक्षण किया है, अन्य संस्करणों के रूप में अच्छी तरह से काम कर सकते हैं):

  • बजना 3.3 - 3.6.0, -Wall -Wextra -std=c++11 -pedantic साथ - जैसा कि ऊपर वर्णित काम करता है।
  • जीसीसी 4.7.3 - 4.9.2, वही विकल्प - ऊपर जैसा ही है। उत्सुकता से, जीसीसी 5.1.0 - 5.2.0 अब कोड के सही संस्करण का उपयोग करके आंशिक विशेषज्ञता का चयन नहीं करता है। यह एक प्रतिगमन की तरह दिखता है। मेरे पास उचित बग रिपोर्ट को एक साथ रखने का समय नहीं है; अगर आप चाहें तो ऐसा करने में संकोच न करें। समस्या टेम्पलेट टेम्पलेट पैरामीटर के साथ पैरामीटर पैक के उपयोग से संबंधित प्रतीत होती है। वैसे भी, जीसीसी enable_if_data<T> का उपयोग कर गलत संस्करण स्वीकार करता है, ताकि यह एक अस्थायी समाधान हो।
  • एमएसवीसी: दृश्य सी ++ 2015, /W4 के साथ, ऊपर वर्णित अनुसार कार्य करता है। पुराने संस्करण डिफ़ॉल्ट तर्क में decltype पसंद नहीं है, लेकिन तकनीक ही अभी भी काम करता है - को व्यक्त करने का एक और तरीका बाधा उस पर 2013 अद्यतन 4.
18

यदि User<> के मूल घोषणा

template<typename, typename=std::true_type> class User; 

अनुकूलित किया जा सकता है तो हम एक समाधान पा सकते हैं (निम्नलिखित ल्यूक डेंटन की टिप्पणी , std::enable_if का उपयोग करने के बजाय)

template<typename> 
struct is_Data : std::false_type {}; 
template<typename T> 
struct is_Data<Data<T>> : std::true_type {}; 

template<typename T> 
class User<T, typename is_Data<T>::type > 
{ /* ... */ }; 

कैसे कभी, यह मूल प्रश्न का उत्तर नहीं देता है, क्योंकि इसे User की मूल परिभाषा को बदलने की आवश्यकता है। मैं अभी भी एक बेहतर उत्तर के लिए प्रतीक्षा कर रहा हूं। यह एक ऐसा हो सकता है जो विशेष रूप से दर्शाता है कि कोई अन्य समाधान संभव नहीं है

+0

समाधान को पोस्ट किए गए लिंक में सही तरीके से प्रदर्शित किया गया है, हालांकि इसे इस उत्तर में सही ढंग से स्थानांतरित/अनुकूलित नहीं किया गया था - यह निम्नलिखित की आंशिक विशेषज्ञता होनी चाहिए: 'टेम्पलेट क्लास उपयोगकर्ता <टी, टाइपनाम std :: enable_if :: मूल्य > :: प्रकार> {...}; '... एक" संपादन "पोस्ट करेगा। – etherice

+0

@etherice धन्यवाद। संपादन में तय – Walter

+0

मुझे लगता है [यह जवाब] (http: // stackoverflow।कॉम/ए/31213703/1269661) मूल परिभाषा – Predelnik

5

जैसा कि आप केवल एक ही शर्त सत्य होने पर इसे लागू करना चाहते हैं, सबसे आसान समाधान स्थिर दावे का उपयोग करना है। यह SFINAE की आवश्यकता नहीं है, एक स्पष्ट संकलन त्रुटि देता है, तो गलत तरीके से इस्तेमाल किया और User<> की घोषणा अनुकूलित किया जा की जरूरत नहीं है:

template<typename T> class User { 
    static_assert(is_Data<T>::value, "T is not (a subclass of) Data<>"); 
    /** Implementation. **/ 
}; 

यह भी देखें: When to use static_assert instead of SFINAE?static_assert एक C++ 11 निर्माण है, लेकिन वहाँ बहुत सारे समाधानों पूर्व C++ 11 compilers के लिए उपलब्ध की तरह हैं,:

#define STATIC_ASSERT(consdition,name) \ 
    typedef char[(condition)?1:-1] STATIC_ASSERT_ ## name 

user<> की घोषणा बदला जा सकता है और आप दो कार्यान्वयन चाहते हैं

template<typename T, bool D=is_Data<T>::value> class User; 

template<typename T> class User<T, true> { 
    static_assert(is_Data<T>::value, "T is not (a subclass of) Data<>"); // Optional 
    /* Data implementation */ 
}; 

template<typename T> class User<T, false> { 
    static_assert(!is_Data<T>::value, "T is (a subclass of) Data<>"); // Optional 
    /* Non-data implementation */ 
}; 

स्थिर दावे केवल चेकों उपयोगकर्ता गलती से टेम्पलेट तर्क D incorre निर्दिष्ट नहीं किया है कि क्या: is_Data के मूल्य पर निर्भर करता है, तो वहाँ भी एक समाधान है कि SFINAE का उपयोग नहीं करता है ctly। यदि D स्पष्ट रूप से निर्दिष्ट नहीं है, तो स्थिर दावे को छोड़ा जा सकता है।

+1

यह वास्तव में मेरी समस्या का समाधान नहीं करता है। मैं अभी भी 'डेटा ' के अन्य विशेषज्ञताओं के लिए अनुमति देना चाहता हूं (उसमें उल्लेख करने के लिए प्रश्न संपादित करेंगे)। – Walter

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