कैसे `void_t` काम करता है


149

मैंने आधुनिक टेम्पलेट प्रोग्रामिंग ( भाग I , भाग II ) के बारे में Cppcon14 में वाल्टर ब्राउन की चर्चा देखी , जहां उन्होंने अपनी void_tSFINAE तकनीक प्रस्तुत की ।

उदाहरण:
एक सरल चर टेम्पलेट को देखते हुए जो मूल्यांकन करता है voidकि सभी टेम्पलेट तर्क अच्छी तरह से बने हैं:

template< class ... > using void_t = void;

और निम्नलिखित लक्षण जो सदस्य चर के अस्तित्व की जांच करता है जिसे सदस्य कहा जाता है :

template< class , class = void >
struct has_member : std::false_type
{ };

// specialized as has_member< T , void > or discarded (sfinae)
template< class T >
struct has_member< T , void_t< decltype( T::member ) > > : std::true_type
{ };

मैंने यह समझने की कोशिश की कि यह क्यों और कैसे काम करता है। इसलिए एक छोटा उदाहरण:

class A {
public:
    int member;
};

class B {
};

static_assert( has_member< A >::value , "A" );
static_assert( has_member< B >::value , "B" );

1। has_member< A >

  • has_member< A , void_t< decltype( A::member ) > >
    • A::member मौजूद
    • decltype( A::member ) अच्छी तरह से गठित है
    • void_t<> मान्य है और इसका मूल्यांकन करता है void
  • has_member< A , void > और इसलिए यह विशेष टेम्पलेट चुनता है
  • has_member< T , void > और का मूल्यांकन करता है true_type

2। has_member< B >

  • has_member< B , void_t< decltype( B::member ) > >
    • B::member अस्तित्व में नहीं है
    • decltype( B::member ) बीमार है और चुपचाप विफल रहता है (sfinae)
    • has_member< B , expression-sfinae > इसलिए इस टेम्पलेट को छोड़ दिया गया है
  • संकलक has_member< B , class = void >डिफ़ॉल्ट तर्क के रूप में शून्य के साथ पाता है
  • has_member< B > का मूल्यांकन करता है false_type

http://ideone.com/HCTlBb

प्रश्न:
1. क्या मेरी यह समझ सही है?
2. वाल्टर ब्राउन का कहना है कि डिफ़ॉल्ट तर्क को ठीक उसी प्रकार का होना चाहिए जिस तरह void_tसे काम करने के लिए इसका उपयोग किया जाता है । ऐसा क्यों है? (मुझे नहीं पता कि इस प्रकार के मिलान की आवश्यकता क्यों है, क्या कोई डिफ़ॉल्ट प्रकार काम नहीं करता है?)


6
विज्ञापन 2) कल्पना करें कि स्थैतिक मुखर के रूप में लिखा गया था has_member<A,int>::value:। फिर, आंशिक विशेषज्ञता जो कि मूल्यांकन करती है has_member<A,void>, मेल नहीं खा सकती है। इसलिए, यह has_member<A,void>::valueसिंथेटिक चीनी, प्रकार के एक डिफ़ॉल्ट तर्क के साथ, या होना चाहिए void
dyp

1
@ धन्यवाद, मैं उसे संपादित करूँगा। Mh, मुझे अभी तक has_member< T , class = void >डिफ़ॉल्ट होने की आवश्यकता नहीं दिखती है void। मान लें कि इस विशेषता का उपयोग किसी भी समय केवल 1 टेम्पलेट तर्क के साथ किया जाएगा, तो डिफ़ॉल्ट तर्क किसी भी प्रकार का हो सकता है?
बकवास

दिलचस्प सवाल।
एएसटोपेर

2
ध्यान दें कि इस प्रस्ताव में, open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4436.pdf , वाल्टर बदल template <class, class = void>गए template <class, class = void_t<>>। इसलिए अब हम void_tउर्फ टेम्पलेट कार्यान्वयन के साथ जो कुछ भी करना चाहते हैं, उसके लिए स्वतंत्र हैं :)
जॉनकॉच

जवाबों:


133

1. प्राथमिक कक्षा टेम्पलेट

जब आप लिखते हैं has_member<A>::value, तो संकलक नाम has_memberदेखता है और प्राथमिक वर्ग टेम्पलेट पाता है , अर्थात यह घोषणा है:

template< class , class = void >
struct has_member;

(ओपी में, यह एक परिभाषा के रूप में लिखा गया है।)

टेम्पलेट तर्क सूची <A>की तुलना इस प्राथमिक टेम्पलेट के टेम्पलेट पैरामीटर सूची से की जाती है। चूंकि प्राथमिक टेम्पलेट में दो पैरामीटर हैं, लेकिन आपने केवल एक की आपूर्ति की है, शेष पैरामीटर डिफ़ॉल्ट टेम्पलेट तर्क के लिए डिफ़ॉल्ट है void:। यह ऐसा है मानो आपने लिखा हो has_member<A, void>::value

2. विशिष्ट वर्ग टेम्पलेट

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

template< class T >
struct has_member< T , void_t< decltype( T::member ) > > : true_type
{ };

संकलक A, voidआंशिक विशेषज्ञता में परिभाषित पैटर्न के साथ टेम्पलेट तर्कों से मेल खाने की कोशिश करता है : Tऔर void_t<..>एक - एक करके। सबसे पहले , टेम्पलेट तर्क कटौती की जाती है। उपर्युक्त आंशिक विशेषज्ञता अभी भी टेम्पलेट-मापदंडों के साथ एक टेम्पलेट है जिसे तर्कों द्वारा "भरा" होने की आवश्यकता है।

पहला पैटर्न T , कंपाइलर को टेम्पलेट-पैरामीटर को कम करने की अनुमति देता है T। यह एक मामूली कटौती है, लेकिन एक पैटर्न पर विचार करें T const&, जहां हम अभी भी कटौती कर सकते हैं T। पैटर्न Tऔर टेम्पलेट तर्क के लिए A, हम Tहोने के लिए कटौती करते हैं A

दूसरे पैटर्न में void_t< decltype( T::member ) > , टेम्प्लेट-पैरामीटर Tएक संदर्भ में प्रकट होता है, जहां इसे किसी भी टेम्प्लेट तर्क से घटाया नहीं जा सकता है।

इसके लिए दो कारण हैं:

  • अंदर की अभिव्यक्ति decltypeको स्पष्ट रूप से टेम्पलेट तर्क कटौती से बाहर रखा गया है। मुझे लगता है कि यह इसलिए है क्योंकि यह मनमाने ढंग से जटिल हो सकता है।

  • यहां तक ​​कि अगर हम बिना पैटर्न का उपयोग करते decltypeहैं void_t< T >, तो Tहल किए गए उर्फ ​​टेम्पलेट पर घटता है। यही है, हम उर्फ ​​टेम्पलेट को हल करते हैं और बाद Tमें परिणामस्वरूप पैटर्न से प्रकार को कम करने का प्रयास करते हैं । परिणामी पैटर्न, हालांकि, voidजो पर निर्भर नहीं है Tऔर इसलिए हमें इसके लिए एक विशिष्ट प्रकार खोजने की अनुमति नहीं देता है T। यह एक स्थिर कार्य (उन शब्दों के गणितीय अर्थ में) को उलटने की कोशिश करने की गणितीय समस्या के समान है।

खाका तर्क कटौती समाप्त हो गया है (*) , अब deduced टेम्पलेट तर्क प्रतिस्थापित कर रहे हैं। यह एक विशेषज्ञता बनाता है जो इस तरह दिखता है:

template<>
struct has_member< A, void_t< decltype( A::member ) > > : true_type
{ };

void_t< decltype( A::member ) >अब प्रकार का मूल्यांकन किया जा सकता है। यह प्रतिस्थापन के बाद अच्छी तरह से बनता है, इसलिए, कोई प्रतिस्थापन विफलता नहीं होती है। हमें मिला:

template<>
struct has_member<A, void> : true_type
{ };

3. चुनाव

अब , हम इस विशेषज्ञता के टेम्प्लेट पैरामीटर सूची की तुलना मूल में दिए गए टेम्प्लेट तर्कों से कर सकते हैं has_member<A>::value। दोनों प्रकार बिल्कुल मेल खाते हैं, इसलिए इस आंशिक विशेषज्ञता को चुना जाता है।


दूसरी ओर, जब हम टेम्पलेट को इस प्रकार परिभाषित करते हैं:

template< class , class = int > // <-- int here instead of void
struct has_member : false_type
{ };

template< class T >
struct has_member< T , void_t< decltype( T::member ) > > : true_type
{ };

हम उसी विशेषज्ञता के साथ समाप्त होते हैं:

template<>
struct has_member<A, void> : true_type
{ };

लेकिन हमारी टेम्प्लेट तर्क सूची has_member<A>::valueअब के लिए है <A, int>। तर्क विशेषज्ञता के मापदंडों से मेल नहीं खाते हैं, और प्राथमिक टेम्पलेट को पतन-बैक के रूप में चुना जाता है।


(*) मानक, IMHO उलझन में, प्रतिस्थापन प्रक्रिया और टेम्पलेट तर्क कटौती प्रक्रिया में स्पष्ट रूप से निर्दिष्ट टेम्पलेट तर्कों के मिलान शामिल हैं। उदाहरण के लिए (N4296 के बाद) [temp.class.spec.match] / 2:

आंशिक विशेषज्ञता किसी दिए गए वास्तविक टेम्पलेट तर्क सूची से मेल खाती है, यदि आंशिक विशेषज्ञता के टेम्पलेट तर्क वास्तविक टेम्पलेट तर्क सूची से काटे जा सकते हैं।

लेकिन इस नहीं है बस मतलब निष्कर्ष निकाला जाना है कि आंशिक विशेषज्ञता के सभी टेम्पलेट-पैरामीटर; इसका अर्थ यह भी है कि प्रतिस्थापन को सफल होना चाहिए और (जैसा लगता है?) टेम्पलेट तर्कों को आंशिक विशेषज्ञता के (प्रतिस्थापित) टेम्पलेट मापदंडों से मेल खाना है। ध्यान दें कि मैं पूरी तरह से अवगत नहीं हूँ जहाँ स्टैंडर्ड प्रतिस्थापित तर्क सूची और आपूर्ति तर्क सूची के बीच तुलना का उल्लेख है।


3
धन्यवाद! मैंने इसे बार-बार पढ़ा है, और मुझे लगता है कि मेरी सोच यह है कि टेम्पलेट तर्क बिल्कुल कैसे काम करता है और अंतिम टेम्पलेट के लिए संकलक क्या चुनता है, फिलहाल सही नहीं है।
निरसन

1
@ जोहान्सचैब-लिट थैंक्स! हालांकि यह थोड़ा निराशाजनक है। क्या विशेषज्ञता के साथ टेम्पलेट तर्क के मिलान के लिए वास्तव में कोई नियम नहीं हैं? स्पष्ट विशेषज्ञता के लिए भी नहीं?
dyp

2
W / r / t डिफ़ॉल्ट टेम्पलेट तर्क, खुले-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2008
TC

1
@ कुछ सप्ताह बाद और इसके बारे में बहुत कुछ पढ़ने और इस स्निपेट से एक संकेत के साथ मुझे लगता है कि मैं यह समझना शुरू कर देता हूं कि यह कैसे काम करता है। आपका स्पष्टीकरण मेरे लिए अधिक समझ से पढ़ने के लिए बनाता है, धन्यवाद!
23

1
मैं जोड़ना चाहता था, कि प्राथमिक टेम्पलेट शब्द कुंजी था (कोड में टेम्पलेट पहली मुठभेड़)
12

18
// specialized as has_member< T , void > or discarded (sfinae)
template<class T>
struct has_member<T , void_t<decltype(T::member)>> : true_type
{ };

यह उपरोक्त विशेषज्ञता केवल तब ही मौजूद होती है जब यह अच्छी तरह से बनती है, इसलिए जब decltype( T::member )यह वैध होता है और अस्पष्ट नहीं होता है। विशेषज्ञता तो has_member<T , void>टिप्पणी में राज्य के लिए है।

जब आप लिखते हैं has_member<A>, तो यह has_member<A, void>डिफ़ॉल्ट टेम्पलेट तर्क के कारण होता है।

और हमारे पास इसके लिए विशेषज्ञता है has_member<A, void>(इसलिए इनहेरिट करें true_type) लेकिन हमारे पास इसके लिए विशेषज्ञता नहीं है has_member<B, void>(इसलिए हम डिफ़ॉल्ट परिभाषा का उपयोग करते हैं: इनहेरिट करें false_type)

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