2012-04-16 7 views
5

यदि मेरे पास कोई ऑब्जेक्ट a है या तो एक अंतर्निहित सरणी या एक वर्ग-प्रकार उपयुक्त operator [] के साथ, और इसके लौटाए गए प्रकार को स्वयं अनुक्रमित किया जा सकता है, तो मुझे एक सामान्य फ़ंक्शन कैसे लिखना चाहिए जो उन सभी को एक भिन्नता के साथ इंडेक्स कर सकता है अलग ब्रैकेट ब्लॉक के बजाय कॉल करें? दूसरे शब्दों में, मैं की तरह भाव बना सकते हैं:कोई एक वैरिएडिक-आधारित जंजीर-इंडेक्सिंग फ़ंक्शन कैसे बना सकता है?

a[i0][i1]...[iK] 

और मैं एक ही समारोह के रूप में यह लिखने के लिए सक्षम होना चाहते हैं:

slice(a, i0, i1, ..., iK) 

सी के नियमों ++ operator [] की आवश्यकता होती है एक तर्क पर काम करने के बाद से , यह विविध पदार्थों के साथ कम अनुकूल है। (यह सवाल एक Usenet धागे पर आधारित है, जहां मैं ऐसी ही कुछ पूछ की कोशिश की है, अपने आप को सुलझाने केवल संभवतः-नेस्ट में निर्मित सरणियों समाप्त।)

एक पहला वार:

template < typename T, typename U > 
constexpr 
auto slice(T &&t, U &&u) noexcept(???) -> ??? 
{ return ce_forward<T>(t)[ ce_forward<U>(u) ]; } 

template < typename T, typename U, typename V, typename ...W > 
constexpr 
auto slice(T &&t, U &&u, V &&v, W... &&w) noexcept(???) -> ??? 
{ 
    return slice(ce_forward<T>(t)[ce_forward<U>(u)], ce_forward<V>(v), 
    ce_forward<W>(w)...); 
} 

ce_forward समारोह टेम्पलेट्स हैं constexpr -मार्क std::forward। (मानक में कुछ चीजें जिन्हें constexpr चिह्नित किया जा सकता है।) मैं रिटर्न प्रकार और अपवाद विनिर्देश स्पॉट्स में डालने के लिए सही चीजों को समझने की कोशिश कर रहा हूं। कुछ मामलों और प्रतिवाद मैं के बारे में पता कर रहे हैं:

  • निर्मित operator [] एक संकार्य की आवश्यकता है एक गणना या पूर्णांक प्रकार होने के लिए एक डाटा-सूचक (या सड़ा हुआ सरणी संदर्भ) और अन्य किया जाना है। ऑपरेंड किसी भी क्रम में हो सकता है। मुझे नहीं पता कि एक ऑपरेंड को एक क्लास-टाइप के साथ एक स्पष्ट प्रकार (गैर-स्पष्ट?) रूपांतरण के साथ एक उचित प्रकार में बदला जा सकता है या नहीं।
  • एक वर्ग प्रकार operator [] को गैर स्थैतिक सदस्य फ़ंक्शन के रूप में परिभाषित कर सकता है। इस तरह के किसी भी फ़ंक्शन में केवल एक पैरामीटर हो सकता है (this के अलावा)।
  • बिल्ट-इन ऑपरेटर फेंक नहीं सकता है। (जब तक ऑपरेंड यूडीटी रूपांतरण पर आधारित नहीं हो सकते हैं; कहा कि रूपांतरण एकमात्र संभावित फेंक बिंदु है।) सीमा त्रुटियों के कारण अनिर्धारित व्यवहार यहां हमारे अधिकार से परे है।
  • क्या अंतिम इंडेक्सिंग कॉल एल-वैल्यू संदर्भ देता है, आर-वैल्यू संदर्भ, या मान slice के रिटर्न प्रकार में प्रतिबिंबित होना चाहिए।
  • यदि (अंतिम?) कॉल मूल्य-दर है, तो std::is_nothrow_move_constructible<ReturnType>::value अपवाद विनिर्देश के लिए OR'd होना चाहिए। (संदर्भ संदर्भ रिटर्न noexcept हैं।)
  • यदि अंतर्निहित ऑपरेटर में एक सरणी शामिल है, तो उस चरण के लिए लौटा संदर्भ आर-वैल्यू संदर्भ होना चाहिए यदि सरणी भी एक है। (यह एक प्रकार का दोष है क्योंकि एरे के विपरीत पॉइंटर्स, अपने लक्ष्य की एल-बनाम आर-वैल्यू स्थिति खो देते हैं, इसलिए पारंपरिक रिटर्न हमेशा एल-वैल्यू संदर्भ होगा।)
  • क्लास-प्रकार इंडेक्सिंग ऑपरेटरों के लिए, सुनिश्चित करें कि अपवाद-spec और रिटर्न-प्रकार अनुभाग एक ही ओवरलोड (यदि एक से अधिक) का संदर्भ लें जो फ़ंक्शन बॉडी का उपयोग करता है। ओवरलोड के लिए सावधान रहें जो केवल this (const/volatile/दोनों/न तो और/या &/&&/न तो) की योग्यता में भिन्न हों।
+0

यह 'constexpr' होना चाहिए? – kennytm

+0

इसके लिए मैं इसका उपयोग कर रहा हूं, जब भी संभव हो, यह 'constexpr' होना चाहिए। – CTMacUser

+1

क्या आपने बस रिटर्न प्रकार और 'अस्वीकरण' विनिर्देश दोनों के लिए 'वापसी' कथन में समान अभिव्यक्ति डालने पर विचार किया है? जैसे 'noexcept (noexcept (std :: declval () [std :: declval ()]) -> decltype (std :: declval () [std :: declval ()])। –

उत्तर

3

मैं @ ल्यूक-डैंटन द्वारा टिप्पणी और @ user315052 द्वारा उत्तर के आधार पर एक समाधान के साथ आया हूं।

#include <utility> 

template < typename Base, typename ...Indices > 
class indexing_result; 

template < typename T > 
class indexing_result<T> 
{ 
public: 
    using type = T; 

    static constexpr 
    bool can_throw = false; 
}; 

template < typename T, typename U, typename ...V > 
class indexing_result<T, U, V...> 
{ 
    using direct_type = decltype(std::declval<T>()[std::declval<U>()]); 
    using next_type = indexing_result<direct_type, V...>; 

    static constexpr 
    bool direct_can_throw 
    = not noexcept(std::declval<T>()[std::declval<U>()]); 

public: 
    using type = typename next_type::type; 

    static constexpr 
    bool can_throw = direct_can_throw || next_type::can_throw; 
}; 

template < typename T > 
inline constexpr 
auto slice(T &&t) noexcept -> T && 
{ return static_cast<T &&>(t); } 

template < typename T, typename U, typename ...V > 
inline constexpr 
auto slice(T &&t, U &&u, V &&...v) 
    noexcept(!indexing_result<T, U, V...>::can_throw) 
    -> typename indexing_result<T, U, V...>::type 
{ 
    return slice(static_cast<T &&>(t)[static_cast<U &&>(u)], 
    static_cast<V &&>(v)...); 
} 

मैंने एक full example program को एक गिस्ट के रूप में रखा है। यह एक वेब साइट कंपाइलर पर जीसीसी> = 4.7, CLang> = 3.2, और इंटेल सी ++> = 13.0 चला रहा है।

+0

'bool direct_can_throw = noexcept (...)' '' 'वहां नहीं होना चाहिए? – 0x499602D2

+0

@ 0x499602D2, हां। 'अस्वीकरण' सामान जानना चाहता है कि हम कब नहीं फेंकते हैं, लेकिन मेरी कक्षा कब फेंकती है। – CTMacUser

+0

ओह हाँ मैं भूल गया था 'नहीं' कीवर्ड '!' जैसा ही था! – 0x499602D2

1

slice फ़ंक्शन के लिए सही वापसी मूल्य प्राप्त करने के लिए, आप एक सही टेम्पलेट बना सकते हैं जो सही परिणाम प्रकार की गणना करता है।उदाहरण के लिए:

template <typename T, typename U, typename... V> 
struct SliceResult { 
    typedef typename SliceResult<T, V...>::Type Type; 
}; 

template <typename T, typename U> 
struct SliceResult<T, U> { 
    typedef typename std::underlying_type<T[U(0)]>::type Type; 
}; 

slice समारोह तो एक SliceResult<...>::Type लौट आते हैं।

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