2015-08-30 10 views
14

के विनिर्देश पर विचार रेंज आधारित पाश के के लिए शुरू-expr और अंत expr (N4140 [stmt.ranged]/p1)। एक सीमा के प्रकार _RangeT की __range,अनुकरण रेंज आधारित पाश के शुरू के लिए/अंत व्यवहार

शुरू-expr और अंत expr इस प्रकार निर्धारित कर रहे हैं को देखते हुए:

  • अगर _RangeT एक सरणी प्रकार है, शुरू-expr और एंड-एक्सप्र क्रमशः __range और __range + __bound हैं, जहां __bound सरणीबद्ध है। यदि _RangeT अज्ञात आकार की एक सरणी है या अधूरा प्रकार की एक सरणी है, तो प्रोग्राम खराब है;
  • अगर _RangeT एक वर्ग प्रकार है, अयोग्य-आईडी रों begin और end वर्ग के सदस्य पहुँच देखने (3.4.5) द्वारा के रूप में अगर वर्ग _RangeT के दायरे में ऊपर देखा जाता है, और या तो (या दोनों) अगर पाता है कम से कम एक घोषणा, शुरू-expr और अंत expr__range.begin() कर रहे हैं और __range.end() क्रमश;
  • अन्यथा, शुरू-expr और अंत exprbegin(__range) और end(__range), क्रमशः, जहां begin और end संबद्ध नेमस्पेस (3.4.2) में देखा जाता है। [नोट: साधारण अयोग्य लुकअप (3.4.1) नहीं किया गया है। - अंत टिप्पणी]

यह साधारण सी ++ कोड में इस सटीक व्यवहार अनुकरण करने के लिए संभव है? जैसे कि, हम लिख सकते हैं एक magic_begin और एक magic_end समारोह टेम्पलेट ऐसी है कि

for(auto&& p : range_init) { /* statements */ } 

और

{ 
    auto&& my_range = range_init; 
    for(auto b = magic_begin(my_range), e = magic_end(my_range); b != e; ++b){ 
     auto&& p = *b; 
     /* statements */ 
    } 
} 

हमेशा ठीक उसी व्यवहार है?

गैर जवाब योग्य कॉल करने के लिए std::begin/std::end (अन्य बातों के अलावा तीसरी गोली, संभाल नहीं करता है) और using std::begin; begin(range);, क्योंकि अन्य बातों के अलावा, कि अस्पष्ट है अगर begin के लिए ADL एक अधिभार कि std::begin के रूप में समान रूप से अच्छा है पाता शामिल ।


उदाहरण के लिए,

namespace foo { 
    struct A { int begin; }; 
    struct B { using end = int; }; 
    class C { int* begin(); int *end(); }; // inaccessible 
    struct D { int* begin(int); int* end();}; 
    struct E {}; 

    template<class T> int* begin(T&) { return nullptr; } 
    template<class T> int* end(T&) { return nullptr; } 
} 

foo::A a; foo::B b; foo::C c; foo::D d; foo::E e; 

दिया मैं magic_begin(a)/magic_begin(b)/magic_begin(c)/magic_begin(d) एक संकलन त्रुटि होना चाहता हूँ, और magic_begin(e)(int*)nullptr वापस जाने के लिए।

+0

आपका मतलब है, आप 'magic_end' को' b' के लिए संकलन त्रुटि होना चाहते हैं, है ना? – Columbo

+0

@ कोल्लमबो ठीक है, अगर हम श्रेणी के लिए श्रेणी-आधारित हैं, तो दोनों त्रुटियां होंगी। लेकिन मैं "जादू_बेजिन 'में से कम से कम एक और' magic_end' के परिणामस्वरूप एक त्रुटि में खुश हूं"। –

+0

ओह, क्षमा करें! दूसरे बुलेट बिंदु को गलत तरीके से पढ़ें। – Columbo

उत्तर

11

निम्नलिखित SFINAE के अनुकूल दृष्टिकोण के रूप में वांछित काम करने के लिए लगता है (अपवाद के लिए नीचे देखें):

#include <type_traits> 

namespace detail { 
    struct empty {}; 
    template <typename T> 
    using base = std::conditional_t<std::is_class<T>{} && not std::is_final<T>{}, 
            T, empty>; 

    struct P1 {typedef int begin, end;}; 
    template <typename U> 
    struct TestMemType : base<U>, P1 { 
     template <typename T=TestMemType, typename=typename T::begin> 
     static std::true_type test_begin(int); 
     template <typename T=TestMemType, typename=typename T::end> 
     static std::true_type test_end(int); 

     static std::false_type test_begin(float), test_end(float); 
    }; 

    template <typename T> 
    constexpr bool hasMember = !decltype(TestMemType<T>::test_begin(0)){} 
          || !decltype(TestMemType<T>::test_end(0)){}; 

    //! Step 1 
    template <typename T, std::size_t N> 
    constexpr auto begin(int, T(&a)[N]) {return a;} 
    template <typename T, std::size_t N> 
    constexpr auto end(int, T(&a)[N]) {return a+N;} 

    //! Step 2 - this overload is less specialized than the above. 
    template <typename T> 
    constexpr auto begin(int, T& a) -> decltype(a.begin()) {return a.begin();} 
    template <typename T> 
    constexpr auto end(int, T& a) -> decltype(a.end()) {return a.end();} 

    //! Step 3 
    namespace nested_detail { 
     void begin(), end(); 
     template <typename T> 
     constexpr auto begin_(T& a) -> decltype(begin(a)) {return begin(a);} 
     template <typename T> 
     constexpr auto end_(T& a) -> decltype(end(a)) {return end(a);} 
    } 
    template <typename T, typename=std::enable_if_t<not hasMember<std::decay_t<T>>>> 
    constexpr auto begin(float, T& a) -> decltype(nested_detail::begin_(a)) 
    {return nested_detail::begin_(a);} 
    template <typename T, typename=std::enable_if_t<not hasMember<std::decay_t<T>>>> 
    constexpr auto end(float, T& a) -> decltype(nested_detail::end_(a)) 
    {return nested_detail::end_(a);} 
} 

template <typename T> 
constexpr auto magic_begin(T& a) -> decltype(detail::begin(0, a)) 
{return detail::begin(0, a);} 
template <typename T> 
constexpr auto magic_end (T& a) -> decltype(detail::end (0, a)) 
{return detail:: end(0, a);} 

Demo। ध्यान दें कि जीसीसी लुकअप टूटा हुआ है क्योंकि यह typename T::begin के लिए TestMemType::test_end/begin में गैर-प्रकार के नामों पर विचार नहीं करता है। एक वर्कअराउंड स्केच here पाया जा सकता है। अगर उन नाम begin/end साथ एक दुर्गम सदस्य है -

चरण 2 में जाँच लें कि वर्ग प्रकार, व्युत्पत्ति जिसका मतलब है कि इस पद्धति ठीक से final वर्ग या यूनियनों के साथ काम नहीं करता हो की आवश्यकता है।

+0

... और यूनियनों। (और गैर-वर्ग के प्रकार, लेकिन यह ठीक करना आसान है।) हालांकि बहुत अच्छा है। –

+0

@ टी.सी. अरे, क्षमा करें। फिक्स्ड है कि गैर-वर्ग गैर-सरणी बिट। – Columbo

1

लगभग।

यदि यह काम करता है तो # 1 कर रहा है, और यदि यह काम करता है तो # 2 नहीं है, और यदि नहीं # 3 एक सुंदर मूल टैग प्रेषण/sfinae व्यायाम है।

# 3 के लिए:

एक namespace कि कहीं और प्रयोग किया जाता है बनाएँ। इसे दूसरे में रखें।

बाहरी में, =delete प्रारंभ करें जो कुछ भी लेता है।

इसमें एक सहायक फ़ंक्शन डालें जो begin पर कॉल करता है।

यह एडीएल शुरू होगा, और अन्यथा हटाए गए प्रारंभ।

विफलता मोड यह है कि नेमस्पेस कहीं और इस्तेमाल किया जा सकता है; इसे रोकने के लिए कोई रास्ता नहीं है।

+0

खैर, # 1 छोटा है, लेकिन मुझे यकीन नहीं है कि मैं इस समय के लिए # 3 को अलग करके, # 2 को पूरी तरह अनुकरण कैसे कर सकता हूं। विशेष रूप से, # 2 कहता है कि यदि कक्षा में किसी भी प्रकार का * कोई * सदस्य है जिसे 'स्टार्ट' या 'एंड' कहा जाता है, तो टाइप या एक्सेसिबिलिटी के बावजूद, तो आप सदस्य कॉल करने का प्रयास करते हैं, और एक फ्री फ़ंक्शन कॉल का प्रयास नहीं करते हैं । –

+0

# 3 तक, मैंने उस विचार के बारे में भी सोचा। मुझे लगता है कि इसे 'शून्य शुरू करना होगा (...) = हटाएं;', ताकि हम एडीएल द्वारा पाई गई लालची टेम्पलेटेड अधिभारों के साथ अस्पष्टता न करें। यह तब भी टूट जाता है जब एडीएल को 'शुरू करें (...);', लेकिन दिया गया है कि ऐसा कोई कार्य लगभग पूरी तरह से बेकार है, मुझे लगता है कि मैं इसके बारे में बहुत चिंतित नहीं हूं। –

+0

@ t.c। हम्म। तो सदस्य var 'start'' आकार (टी :: शुरू) 'काम करेगा? सदस्य, समारोह, typedef ...इस मामले में कार्य को संकलित करने में असफलता शामिल है, क्योंकि हम पहले जांच सकते हैं कि 't.begin() 'संकलित करता है और वह चेक नहीं करता है। हां, एक छद्म-सिफीना अभिव्यक्ति पाएं, और अगर यह विफल हो जाए तो 3 जारी रहेगा, और संकलित करने में असफल हो जाता है यदि यह सफल हो जाता है * या * यदि यह संकलित करने में विफल रहता है। – Yakk

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