2015-03-24 20 views
6

अद्यतनकैसे static_assert SFINAE

साथ अच्छा खेलने बनाने के लिए मैं सवाल के जवाब के रूप rebind की एक काम किसी न किसी मसौदा तैनात। हालांकि मुझे मेटाफंक्शन को तोड़ने से static_assert एस रखने के लिए एक सामान्य तरीका खोजने में बहुत भाग्य नहीं था।


मूल रूप से मैं एक टेम्प्लेटेड प्रकार T<U, Args...> कुछ अन्य प्रकार T<V, Args...> से निर्माण किया जा सकता है, तो जाँच करने के लिए चाहते हैं। जहां T और Args... दोनों प्रकारों में समान है। समस्या यह है कि, T<> में static_assert हो सकता है जो पूरी तरह से मेरे मेटाफंक्शन को तोड़ देता है।

नीचे मैं जो करने की कोशिश कर रहा हूं उसका एक संक्षिप्त सारांश है।

template<typename T> 
struct fake_alloc { 
    using value_type = T; 
}; 

template<typename T, typename Alloc = fake_alloc<T>> 
struct fake_cont { 
    using value_type = T; 
    // comment the line below out, and it compiles, how can I get it to compile without commenting this out??? 
    static_assert(std::is_same<value_type, typename Alloc::value_type>::value, "must be the same type"); 
}; 

template<typename T, typename U, typename = void> 
struct sample_rebind { 
    using type = T; 
}; 

template<template<typename...> class Container, typename T, typename U, typename... OtherArgs> 
struct sample_rebind< 
    Container<T, OtherArgs...>, 
    U, 
    std::enable_if_t< 
     std::is_constructible< 
      Container<T, OtherArgs...>, 
      Container<U, OtherArgs...> 
     >::value 
    > 
> 
{ 
    using type = Container<U, OtherArgs...>; 
}; 

static_assert(
    std::is_same< 
     fake_cont<int, fake_alloc<int>>, 
     typename sample_rebind<fake_cont<int>, double>::type 
    >::value, 
    "This should pass!" 
); 

आप वांछित व्यवहार देख सकते हैं कि अंतिम static_assert पास करना चाहिए, लेकिन दुर्भाग्य से, यह भी है कि बात करने के लिए नहीं मिलता है के रूप में fake_cont में static_assert शुरू हो रहा है जब std::is_constructible<> प्रयास fake_cont के निर्माता कॉल करने के लिए है ।

वास्तविक कोड fake_cont में libC++'s std::vector है, इसलिए मैं इसे गड़बड़ नहीं कर सकता, या std::is_constructible की गड़बड़ी नहीं कर सकता।

इस विशिष्ट मुद्दे के आसपास काम करने के लिए कोई सलाह की सराहना की जाती है, और static_assert के आसपास SFINAE के लिए सामान्य रूप से कोई सलाह विशेष रूप से सराहना की जाती है।

संपादित करें: is_same के पहले भाग किया जाना चाहिए था fake_cont<int, fake_alloc<int>>

संपादित करें 2: आप fake_cont में static_assert बाहर टिप्पणी है, यह (बजना 3.5) संकलित करता है। और यही वही है जो मैं चाहता हूं। इसलिए fake_cont में static_assert से बचने के लिए मुझे बस कुछ रास्ता चाहिए।

+0

आपका प्रश्न उदाहरण आप दे काम कभी नहीं होगा के रूप में स्पष्ट नहीं है। क्या आपका मतलब है 'sample_rebind , int> '? और इसके परिणामस्वरूप 'fake_cont >' यदि आप 'अन्य आर्ट्स' रखते हैं ... '। चूंकि यह वर्तमान में खड़ा है, संकलक आपके कोड को अस्वीकार करने के लिए बस सही है। –

+0

मैंने 'fake_cont' में 'static_assert' को टिप्पणी की, और यह वास्तव में अंतिम static_assert को पार करते हुए संकलित (कम से कम 3.5 पर संकलित) करता है। मैं यह नहीं कह रहा कि संकलक गलत है, मैं सिर्फ यह पूछ रहा हूं कि मैं विशेषज्ञता के 'is_constructible' भाग पर संकलक कैसे विफल नहीं कर सकता। ध्यान दें कि यदि प्रकार का निर्माण नहीं किया गया है तो 'sample_rebind' मूल प्रकार – xcvr

+0

लौटाता है क्योंकि यह गैर-विशिष्ट मामले (जिसे तब लिया जाता है) में' टाइप प्रकार = टी; 'का उपयोग किया जाता है। आपने इसे पास करने के लिए अभी तीन त्रुटियों को जोड़ा है! उनमें से किसी को हटा दें और यह असफल हो जाएगा। –

उत्तर

1
namespace details { 
    template<class T,class=void> 
    struct extra_test_t: std::true_type {}; 
} 

हम तो में एक अतिरिक्त परीक्षण गुना:

template<class...>struct types{using type=types;}; 

template<template<typename...> class Container, typename T, typename U, typename... OtherArgs> 
struct sample_rebind< 
    Container<T, OtherArgs...>, 
    U, 
    std::enable_if_t< 
    details::extra_test_t< types< Container<T, OtherArgs...>, U > >::value 
    && std::is_constructible< 
     Container<T, OtherArgs...>, 
     Container<U, OtherArgs...> 
    >::value 
    > 
> { 
    using type = Container<U, OtherArgs...>; 
}; 

और हम अतिरिक्त परीक्षण लिखें:

namespace details { 
    template<class T, class Alloc, class U> 
    struct extra_test_t< 
    types<std::vector<T,Alloc>, U>, 
    typename std::enable_if< 
     std::is_same<value_type, typename Alloc::value_type>::value 
    >::type 
    > : std::true_type {}; 
    template<class T, class Alloc, class U> 
    struct extra_test_t< 
    types<std::vector<T,Alloc>, U>, 
    typename std::enable_if< 
     !std::is_same<value_type, typename Alloc::value_type>::value 
    >::type 
    > : std::false_type {}; 
} 

मूल रूप से, यह हमें हमारे परीक्षण से मिलान करने पर "पैच" इंजेक्षन की सुविधा देता है static_assert

अगर हम is_std_container<T> और get_allocator<T> था, हम लिख सकते हैं:

namespace details { 
    template<template<class...>class Z,class T, class...Other, class U> 
    struct extra_test_t< 
    types<Z<T,Other...>>, U>, 
    typename std::enable_if< 
     is_std_container<Z<T,Other...>>>::value 
     && std::is_same< 
     value_type, 
     typename get_allocator<Z<T,Other...>>::value_type 
     >::value 
    >::type 
    > : std::true_type {}; 
    template<class T, class Alloc, class U> 
    struct extra_test_t< 
    types<std::vector<T,Alloc>, U>, 
    typename std::enable_if< 
     is_std_container<Z<T,Other...>>>::value 
     && !std::is_same< 
     value_type, 
     typename get_allocator<Z<T,Other...>>::value_type 
     >::value 
    >::type 
    > : std::false_type {}; 
} 

या हम सिर्फ राज्य सकता है कि एक allocator_type शायद के साथ कुछ भी पलटाव नहीं हो सकता।

इस समस्या का एक अधिक कंटेनर अवगत दृष्टिकोण संभाजक प्रकार (::allocator_type) निकालने, और U किसी भी तरह के T के rebind साथ कंटेनर तर्क सूची में संभाजक प्रकार के सभी उदाहरणों को बदलने के लिए किया जाएगा। यह अभी भी मुश्किल है, क्योंकि std::map<int, int> में std::allocator< std::pair<const int, int> > प्रकार का आवंटन है, और कुंजी int के बीच अंतर और मूल्य int सामान्य तरीके से संभव नहीं है।

+0

':: allocator_type' को बदलने के बारे में टिप्पणी एक अच्छा विचार है, और मानचित्र के बारे में आपकी चेतावनी ऐसा लगता है जैसे इसे चारों ओर काम किया जा सकता है (' allocator_template < std :: जोड़ी/tuple <...>> ')। लेकिन 'std :: vector > 'इसे तोड़ देगा। मैं इस सप्ताह के अंत में कोशिश करूँगा और काम करूंगा। – xcvr

1

मैं पुनर्जन्म के एक ठोस ठोस पहले मसौदे को पाने में कामयाब रहा हूं। यह सभी एसटीएल कंटेनर (टेम्पलेट पैरामीटर के कम आम संयोजन को छोड़कर), कंटेनर एडेप्टर, और std::integer_sequence के लिए काम करता है। और यह शायद बहुत अधिक चीजों के लिए भी काम करता है। लेकिन यह निश्चित रूप से सब कुछ के लिए काम नहीं करेगा।

मुख्य समस्या को मानचित्र की तरह काम करने के लिए यक के भविष्यवाणी के रूप में काम करना पड़ रहा था, लेकिन थोड़ी सी विशेषता के साथ मदद मिली।

तो कोड के लिए पर ...

void_t

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

वाल्टर ई ब्राउन ने इस छोटे चाल बनाता प्रकार को लागू करने के लिए एक बहुत आसान लक्षण।

प्रकार लक्षण

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

template<template<class...> class C, class First, class Second, class... Others> 
struct is_map_like<C<First, Second, Others...>, 
        std::enable_if_t<std::is_same<typename C<First, Second, Others...>::value_type::first_type, 
               std::add_const_t<First>>{} && 
            std::is_same<typename C<First, Second, Others...>::value_type::second_type, 
               Second>{}>> 
    : std::true_type {}; 

template<class T, class U, class = void> 
struct has_mem_rebind : std::false_type {}; 

template<class T, class U> 
struct has_mem_rebind<T, U, void_t<typename T::template rebind<U>>> : std::true_type {}; 

template<class T> 
struct is_template_instantiation : std::false_type {}; 

template<template<class...> class C, class... Others> 
struct is_template_instantiation<C<Others...>> : std::true_type {}; 
  1. is_map_like तथ्य यह है कि एसटीएल में नक्शे की तरह प्रकार के सभी value_type एक (एन) होने के लिए परिभाषित किया है का उपयोग करता है std::pairconst साथ एड की पहली टेम्पलेट पैरामीटर नक्शा की तरह प्रकार, first_typepair में। मानचित्र-जैसे प्रकार का दूसरा टेम्पलेट पैरामीटर बिल्कुल pair के second_type से मेल खाता है। rebind को नक्शा-तरह के प्रकारों को अधिक सावधानी से संभालना है।
  2. has_mem_rebind चाल का उपयोग कर T पर सदस्य rebind मेटा-फ़ंक्शन की उपस्थिति का पता लगाता है। यदि किसी वर्ग में rebind है तो हम पहले कक्षा कार्यान्वयन को रोक देंगे।
  3. is_template_instantiation पता लगाता है कि T एक टेम्पलेट तत्काल है या नहीं। यह डिबगिंग के लिए अधिक है।

हेल्पर प्रकार सूची

template<class... Types> 
struct pack 
{ 
    template<class T, class U> 
    using replace = pack< 
     std::conditional_t< 
      std::is_same<Types, T>{}, 
      U, 
      Types 
     >... 
    >; 
    template<class T, class U> 
    using replace_or_rebind = pack< 
     std::conditional_t< 
      std::is_same<Types, T>{}, 
      U, 
      typename rebind<Types, U>::type 
     >... 
    >; 
    template<class Not, class T, class U> 
    using replace_or_rebind_if_not = pack< 
     std::conditional_t< 
      std::is_same<Types, Not>{}, 
      Types, 
      std::conditional_t< 
       std::is_same<Types, T>{}, 
       U, 
       typename rebind<Types, U>::type 
      > 
     >... 
    >; 

    template<class T> 
    using push_front = pack<T, Types...>; 
}; 

इस प्रकार के जोड़तोड़ की तरह कुछ सरल सूची

  1. replace एक गैर पुनरावर्ती फैशन में U साथ T की सभी घटनाओं की जगह संभालती है।
  2. replace_or_rebindU साथ T की सभी घटनाओं की जगह है, और सभी मेल नहीं खाने वाले घटनाओं के लिए, कॉल rebind
  3. replace_or_rebind_if_notreplace_or_rebind रूप में ही है, लेकिन किसी भी मिलान Not
  4. push_front बस सामने करने के लिए पर एक तत्व धक्का तत्व पर छोड़ देता है टाइप-सूची

के कॉलिंग सदस्य rebind

// has member rebind implemented as alias 
template<class T, class U, class = void> 
struct do_mem_rebind 
{ 
    using type = typename T::template rebind<U>; 
}; 

// has member rebind implemented as rebind::other 
template<class T, class U> 
struct do_mem_rebind<T, U, void_t<typename T::template rebind<U>::other>> 
{ 
    using type = typename T::template rebind<U>::other; 
}; 

यह मानता है कि मानक के अनुसार सदस्य rebind को लागू करने के दो अलग-अलग मान्य तरीके हैं। allocators के लिए यह rebind<T>::other है। pointers के लिए यह सिर्फ rebind<T> है। do_mem_rebind का यह कार्यान्वयन rebind<T>::other के साथ आता है यदि अन्यथा यह मौजूद है, अन्यथा यह rebind<T> पर आसान हो जाता है।

अनपैक

template<template<class...> class C, class Pack> 
struct unpack; 

template<template<class...> class C, class... Args> 
struct unpack<C, pack<Args...>> { using type = C<Args...>; }; 

template<template<class...> class C, class Pack> 
using unpack_t = typename unpack<C, Pack>::type; 

यह एक pack लेता है, प्रकार इसमें निकालता है, और उन्हें कुछ अन्य टेम्पलेट C में डालता है।

rebind कार्यान्वयन

अच्छी चीजें।

template<class T, class U, bool = is_map_like<T>{}, bool = std::is_lvalue_reference<T>{}, bool = std::is_rvalue_reference<T>{}, bool = has_mem_rebind<T, U>{}> 
struct rebind_impl 
{ 
    static_assert(!is_template_instantiation<T>{}, "Sorry. Rebind is not completely implemented."); 
    using type = T; 
}; 

// map-like container 
template<class U, template<class...> class C, class First, class Second, class... Others> 
class rebind_impl<C<First, Second, Others...>, U, true, false, false, false> 
{ 
    using container_type = C<First, Second, Others...>; 
    using value_type = typename container_type::value_type; 
    using old_alloc_type = typename container_type::allocator_type; 

    using other_replaced = typename pack<Others...>::template replace_or_rebind_if_not<old_alloc_type, First, typename U::first_type>; 

    using new_alloc_type = typename std::allocator_traits<old_alloc_type>::template rebind_alloc<std::pair<std::add_const_t<typename U::first_type>, typename U::second_type>>; 
    using replaced = typename other_replaced::template replace<old_alloc_type, new_alloc_type>; 

    using tail = typename replaced::template push_front<typename U::second_type>; 
public: 
    using type = unpack_t<C, typename tail::template push_front<typename U::first_type>>; 
}; 

// has member rebind 
template<class T, class U> 
struct rebind_impl<T, U, false, false, false, true> 
{ 
    using type = typename do_mem_rebind<T, U>::type; 
}; 

// has nothing, try rebind anyway 
template<template<class...> class C, class T, class U, class... Others> 
class rebind_impl<C<T, Others...>, U, false, false, false, false> 
{ 
    using tail = typename pack<Others...>::template replace_or_rebind<T, U>; 
public: 
    using type = unpack_t<C, typename tail::template push_front<U>>; 
}; 

// has nothing, try rebind anyway, including casting NonType template parameters 
template<class T, template<class, T...> class C, class U, T FirstNonType, T... Others> 
struct rebind_impl<C<T, FirstNonType, Others...>, U, false, false, false, false> 
{ 
    using type = C<U, U(FirstNonType), U(Others)...>; 
}; 

// array takes a non-type parameter parameters 
template<class T, class U, std::size_t Size> 
struct rebind_impl<std::array<T, Size>, U, false, false, false, false> 
{ 
    using type = std::array<U, Size>; 
}; 

// pointer 
template<class T, class U> 
struct rebind_impl<T*, U, false, false, false, false> 
{ 
    using type = typename std::pointer_traits<T*>::template rebind<U>; 
}; 

// c-array 
template<class T, std::size_t Size, class U> 
struct rebind_impl<T[Size], U, false, false, false, false> 
{ 
    using type = U[Size]; 
}; 

// c-array2 
template<class T, class U> 
struct rebind_impl<T[], U, false, false, false, false> 
{ 
    using type = U[]; 
}; 

// lvalue ref 
template<class T, class U> 
struct rebind_impl<T, U, false, true, false, false> 
{ 
    using type = std::add_lvalue_reference_t<std::remove_reference_t<U>>; 
}; 

// rvalue ref 
template<class T, class U> 
struct rebind_impl<T, U, false, false, true, false> 
{ 
    using type = std::add_rvalue_reference_t<std::remove_reference_t<U>>; 
}; 
  1. मामले असफल rebind के लिए बस प्रकार अपरिवर्तित रखने के लिए है। यह rebind<Types, double>... पर कॉल करने की अनुमति देता है कि TypeTypesrebind सक्षम है या नहीं। यदि इसमें टेम्पलेट इंस्टेंटेशन प्राप्त होता है तो वहां static_assert है। यदि यह हिट है, तो आपको शायद rebind
  2. का एक और विशेषज्ञता चाहिए। नक्शा की तरह rebindrebind<std::map<int, int>, std::pair<double, std::string>> जैसे मानचित्र की अपेक्षा की जाती है। तो जिस प्रकार आवंटक को रिबाउंड किया जा रहा है, उस प्रकार से मेल नहीं खा रहा है जिस पर कंटेनर को रिबाउंड किया जा रहा है। यह if_notallocator_type होने के साथ, कुंजी और मान प्रकार को छोड़कर सभी प्रकारों पर replace_or_rebind_if_not करता है। चूंकि आवंटक प्रकार कुंजी/मान जोड़ी से भिन्न होता है rebind जोड़ी के पहले तत्व के const नेस को संशोधित करने की आवश्यकता है। यह आवंटक को पुनर्निर्मित करने के लिए std::allocator_traits का उपयोग करता है, क्योंकि सभी आवंटकों को std::allocator_traits के माध्यम से पुन: प्रयोज्य होना चाहिए।
  3. यदि T में सदस्य rebind है, तो इसका उपयोग करें।
  4. T कोई भी सदस्य rebind, replace_or_rebind से मेल खाने वाले C के पहले टेम्पलेट पैरामीटर टेम्पलेट C के सभी मापदंडों है।
  5. यदि T में एक प्रकार का पैरामीटर है, और गैर-प्रकार टेम्पलेट पैरामीटर का एक गुच्छा जिसका प्रकार पैरामीटर से मेल खाता है। उन सभी गैर-प्रकार पैरामीटर को U पर पुन: प्रयास करने का प्रयास करें। यह वह मामला है जो std::integer_sequence काम करता है।
  6. std::array के लिए एक विशेष मामला आवश्यक था क्योंकि इसमें एक गैर-प्रकार का टेम्पलेट पैरामीटर होता है जो इसे आकार देता है, और वह टेम्पलेट पैरामीटर अकेला छोड़ा जाना चाहिए।
  7. यह मामला अन्य पॉइंटर प्रकारों के पॉइंटर्स को रिबाइंड करने की अनुमति देता है। यह इसे पूरा करने के लिए std::pointer_traits के rebind का उपयोग करता है। T[5]
  8. के बिना एक आकार पूर्व सी सरणियों पर rebind काम करने देता है: T[]
  9. rebind रों lvalue-रेफरी T एक गारंटीकृत lvalue-रेफरी std::remove_reference_t<U> करने के लिए प्रकार
  10. पर सी सरणियों आकार पूर्व rebind काम करने देता है।
  11. rebind s rvalue-ref T प्रकार एक गारंटीकृत रावल्यू-std::remove_reference_t<U> पर रीफ्रेश करें।

व्युत्पन्न (उजागर) वर्ग

template<class T, class U> 
struct rebind : details::rebind_impl<T, U> {}; 

template<class T, class U> 
using rebind_t = typename rebind<T, U>::type; 

वापस SFINAE करने के लिए और static_assert

ज्यादा googling वहाँ की तरह चारों ओर static_assert रों SFINAE लिए एक क्रियाशील रास्ता होना प्रतीत नहीं होता है के बाद libC++ के एसटीएल कंटेनर में से एक। यह वास्तव में मेरी इच्छा करता है कि भाषा में कुछ और अधिक SFINAE दोस्ताना हों, लेकिन अवधारणाओं की तुलना में थोड़ा अधिक विज्ञापन-प्रसार।

तरह:

template<class T> 
    static_assert(CACHE_LINE_SIZE == 64, "") 
struct my_struct { ... }; 
संबंधित मुद्दे