2013-06-30 5 views
9

में स्थित खाके की एक टेम्पलेट उर्फ ​​का उपयोग करने के बजाय एक पिछले प्रश्न से:एक टेम्पलेट

Doing a static_assert that a template type is another template

एंडी छिपकर मुझे इस कोड है कि मुझे static_assert करने की अनुमति देता है कि एक टेम्पलेट प्रकार अन्य टेम्पलेट प्रकार है के साथ प्रदान की:

template<template<typename...> class TT, typename... Ts> 
struct is_instantiation_of : public std::false_type { }; 

template<template<typename...> class TT, typename... Ts> 
struct is_instantiation_of<TT, TT<Ts...>> : public std::true_type { }; 

template<typename T> 
struct foo {}; 

template<typename FooType> 
struct bar { 
    static_assert(is_instantiation_of<foo,FooType>::value, ""); //success 
}; 

int main(int,char**) 
{ 
    bar<foo<int>> b; //success 
    return 0; 
} 

यह बहुत अच्छा काम करता है।

लेकिन अगर मैं foo का एक अन्य नाम का उपयोग करने के लिए इस तरह कोड बदलने के लिए, कुछ बुरा जाना:

template<template<typename...> class TT, typename... Ts> 
struct is_instantiation_of : public std::false_type { }; 

template<template<typename...> class TT, typename... Ts> 
struct is_instantiation_of<TT, TT<Ts...>> : public std::true_type { }; 

template<typename T> 
struct foo {}; 

//Added: alias for foo 
template<typename T> 
using foo_alt = foo<T>; 

template<typename FooType> 
struct bar { 
    //Changed: want to use foo_alt instead of foo here 
    static_assert(is_instantiation_of<foo_alt,FooType>::value, ""); //fail 
}; 

int main(int,char**) { 
    //both of these fail: 
    bar<foo<int>> b; 
    bar<foo_alt<int>> b2; 

    return 0; 
} 

इस हल किया जा सकता है?

+0

हम्म, ऐसा लगता है कि 'foo_alt' एक * typedef-name * है और एक * टेम्पलेट-नाम * नहीं है ... लेकिन यह केवल आपके' बार' को प्रभावित करता है; आप 'मुख्य' के रूप में इसे रख सकते हैं। –

+0

मुझे लगता है कि आप जांच सकते हैं कि दो प्रकार एक ही टेम्पलेट के तत्काल हैं, यह उपनाम टेम्पलेट्स के साथ भी काम करता है। – dyp

उत्तर

6

नहीं, इसे हल नहीं किया जा सकता है (कम से कम डिजाइन को बदलने के बिना नहीं)।

एक टेम्पलेट आईडी एक उपनाम टेम्पलेट की विशेषज्ञता को दर्शाता है, यह है: समस्या यह है कि टेम्पलेट उर्फ ​​नाम निष्कर्ष निकाला नहीं कर रहे हैं, के रूप में पैरा सी ++ 11 स्टैंडर्ड की 14.5.7/2 में बताया गया है उपनाम टेम्पलेट के प्रकार-आईडी में टेम्पलेट-पैरामीटर के लिए टेम्पलेट-तर्कों के प्रतिस्थापन द्वारा प्राप्त संबंधित प्रकार के समतुल्य। -

template<class T> struct Alloc {/.../}; 
template<class T> using Vec = vector<T, Alloc<T>>; 
Vec<int> v; // same as vector<int, Alloc<int>> v; 

... 

template<template<class> class TT> 
void f(TT<int>); 
f(v); // error: Vec not deduced       <=== Relevant 

... 

:

[उदाहरण:

पैरा भी एक उदाहरण देता है: [नोट उपनाम वह टेम्पलेट का नाम deduced. अंत टिप्पणी कभी नहीं है] अंत उदाहरण]

आपके ठोस मामले में, समस्या यह है कि आंशिक विशेषज्ञता से मेल खाने का प्रयास करते समय, संकलक यह नहीं समझ पाएगा कि आपका प्रकार foo_alt का एक तात्कालिकता है (foo_alt एक उपनाम टेम्पलेट का नाम है), और प्राथमिक टेम्पलेट उठाया जाता है

आप उर्फ ​​टेम्पलेट्स का उपयोग करना चाहते हैं, तो आप genericity का एक सा छोड़ देना और foo के लिए एक प्रकार विशेषता विशिष्ट बनाना पड़ेगा:

#include <type_traits> 

template<typename T> 
struct foo {}; 

template<typename T> 
struct is_instantiation_of_foo : std::false_type { }; 

template<typename...Ts> 
struct is_instantiation_of_foo<foo<Ts...>> : std::true_type { }; 

जो तुम तो इस तरह से इस्तेमाल कर सकते हैं:

template<typename FooType> 
struct bar { 
    static_assert(is_instantiation_of_foo<FooType>::value, ""); //fail 
}; 

अब, निम्नलिखित कार्यक्रम में दावे से कोई भी होगा आग:

template<typename T> 
using foo_alt = foo<T>; 

int main(int,char**) { 
    // None of these fail: 
    bar<foo<int>> b; 
    bar<foo_alt<int>> b2; 

    return 0; 
} 

यहां live example है।

+0

शायद हम बेहतर कर सकते हैं। 'SFINAE'-सुरक्षित पैटर्न मिलान 'टेम्पलेट' 'एम' को कम करता है जैसे पारित प्रकार '' 'कुछ' Ts ... 'के लिए है। फिर 'std :: is_same , एम >' की जांच करें, 'एस' बीमार होने पर एसएफआईएनएई भी संरक्षित है, जहां 'एक्स <> 'मिलान करने के लिए पास किया गया' टेम्पलेट 'है ... – Yakk

+0

@Yakk: आप मेरे लिए बहुत प्रतिभाशाली हैं :) मुझे समझ में नहीं आता कि "SFINAE-deduce' का अर्थ क्या है, और मुझे डर है कि मुझे इसे प्राप्त करने से कुछ मिनट पहले समाधान पर घूरने की आवश्यकता होगी;) –

+0

dyn का उत्तर देखें: मैं स्पष्ट से कम था, और इस कीबोर्ड पर कोड लिखना मुश्किल है। मेरा मतलब था "एक SFINAE सुरक्षित तरीके से कटौती करें", इसलिए यदि यह मिलान करने में असफल रहा तो बस 'झूठा' कहता है। डिन ने मुझे जो दोष याद किया वह देखा: वह बहुत स्मार्ट है मेरे लिए! – Yakk

1

यदि आपके पास कोई उपनाम टेम्पलेट है जो निर्दिष्ट वर्ग के टेम्पलेट पैरामीटर को बदलता है (जैसे कि आपके उदाहरण में; यह केवल निर्दिष्ट टेम्पलेट का नाम बदलता है), तो आप कुछ ऐसा उपयोग कर सकते हैं (यह सबसे सुंदर तरीका नहीं है) उनका कहना है कि आप करने के क्रम में की जांच एक SFINAE प्रकार की जरूरत है केवल static_assert असफल जाने के लिए Yakk को

template < template<class...> class TT0, template<class...> class TT1, 
      class... Ts1 > 
struct is_from_same_template_helper 
{ 
    template < class T = TT0<Ts1...>, 
       class = typename std::enable_if< 
        std::is_same<TT0<Ts1...>, TT1<Ts1...>>::value 
       >::type 
      > 
    static std::true_type test(int); 

    template < class T = int > 
    static std::false_type test(...); 
}; 


template<template<class...> class, class> 
struct is_instantiation_of : public std::false_type { }; 

template<template<class...> class TT0, template<class...> class TT1, 
     class... Ts1> 
struct is_instantiation_of<TT0, TT1<Ts1...>> 
    : public decltype(is_from_same_template_helper<TT0, TT1, Ts1...> 
         ::template test<>(0)) 
{ }; 

धन्यवाद (पिछले संस्करण static_assert से पहले एक संकलक त्रुटि के कारण विफल)।

चूंकि उपनाम टेम्पलेट्स की विशेषज्ञता केवल निर्दिष्ट टेम्पलेट की विशेषज्ञता के बराबर होती है; उपनाम टेम्पलेट स्वयं निर्दिष्ट टेम्पलेट के बराबर नहीं है।

यदि उपनाम टेम्पलेट टेम्पलेट पैरामीटर को बदलता है, तो उपरोक्त कार्यान्वयन झूठी नकारात्मक बना सकता है।


एक xy-समस्या के रूप में, आप करने के लिए अपने कार्यान्वयन को बदल सकता है:

#include <type_traits> 


template < template <typename...> class TT0, template <typename...> class TT1 > 
struct is_same_template : public std::false_type { }; 

template < template <typename...> class TT > 
struct is_same_template < TT, TT > : public std::true_type { }; 


template < typename T0, typename T1 > 
struct is_from_same_template : public std::false_type { }; 

template < template <typename...> class TT0, template <typename...> class TT1, 
      typename... Ts0, typename... Ts1 > 
struct is_from_same_template < TT0<Ts0...>, TT1<Ts1...> > 
    : public is_same_template<TT0, TT1> { }; 


template<typename T> 
struct foo {}; 

//Added: alias for foo 
template<typename T> 
using foo_alt = foo<T>; 

template<typename FooType> 
struct bar { 
    //Changed: want to use foo_alt instead of foo here 
    static_assert(is_from_same_template<foo_alt<int>, FooType>::value, ""); 
}; 

int main(int,char**) { 
    //both of these succeed: 
    bar<foo<int>> b; 
    bar<foo_alt<int>> b2; 

    return 0; 
} 

बेशक, आप इस दृष्टिकोण के लिए foo_alt का एक मान्य इन्स्टेन्शियशन की आवश्यकता है।

+0

मैं समझ नहीं पा रहा हूं कि उद्देश्य क्या है 'is_from_same_template_helper' टेम्पलेट के' TT1' टेम्पलेट पैरामीटर का। – Constructor

+0

@ कन्स्ट्रक्टर जैसा कि मैंने अपनी पिछली टिप्पणी में "घोषणा" की है, कोड में एक बग रहा है (हार्ड से SFINAE त्रुटि में बदलते समय संपादन इतिहास के अनुसार पेश किया गया है)। क्या यह अभी स्पष्ट है? – dyp

+0

ओह, क्षमा करें, मैंने आपकी टिप्पणी नहीं देखी। अब यह स्पष्ट है, धन्यवाद। जैसा कि मैंने देखा है कि आप यह तय नहीं कर सकते कि आप कौन सा कीवर्ड अधिक पसंद करते हैं ('टाइपनाम 'या' कक्षा')। :-) – Constructor

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