2015-02-08 9 views
5

मैं रचना का उपयोग करना चाहता हूं और सी ++ क्षमताओं का उपयोग करके प्रत्येक संभावित ओवरलोड (नोएसेप्ट, कॉन्स, अस्थिर) के लिए अच्छी अग्रेषण विधियों को लिखना चाहता हूं।विरासत की बजाय संरचना के साथ विधि अग्रेषण (सी ++ लक्षणों का उपयोग करके)

विचार यह निर्धारित करने के लिए गुणों का उपयोग करना है कि कोई तरीका घोषित किया गया है {noexcept/const/volatile/etc} और तदनुसार व्यवहार करना है।

यहाँ मैं प्राप्त करना चाहते हैं का एक उदाहरण है:

// forward with noexcept attribute 
// I'm not 100% sure about : std::declval<std::add_lvalue_reference<decltype(obj)>::type 

template<typename... Args> 
constexpr decltype(auto) get(Args && ... args) 
noexcept(
     noexcept(std::declval<std::add_lvalue_reference<decltype(obj)>::type>().get( std::forward<Args>(args)... )) 
     and 
     std::is_nothrow_move_constructible<decltype(std::declval<std::add_lvalue_reference<decltype(obj)>::type>().get( std::forward<Args>(args)... ))>::value 
     ) 
{ 
    cout << "const called...\n"; 
    return obj.get(std::forward<Args>(args)...); 
} 

// forward with noexcept and const attributes 
// I'm not sure that this one behave properly. 

template<typename... Args> 
constexpr decltype(auto) get(Args && ... args) 
const noexcept(
     noexcept(std::declval< std::add_const<decltype(obj) &>::type >().get( std::forward<Args>(args)... )) 
     and 
     std::is_nothrow_move_constructible<decltype(std::declval< std::add_const<decltype(obj) &>::type >().get( std::forward<Args>(args)... ))>::value 
     ) 
{ 
    cout << "const not called...\n"; 
    using const_type = std::add_lvalue_reference<std::add_const<std::remove_reference<decltype(obj)>::type>::type>::type; 
    return const_cast<const_type>(obj).get(std::forward<Args>(args)...); 
} 

कृपया ध्यान दें कि इस प्रश्न का अनुसरण से अलग है:

struct User{  
    UsedObject& obj; 
    User(UsedObject& obj) : obj(obj) {} 

    FORWARD_METHOD(obj, get); //here is where the forwarding happens 
}; 

struct UsedObject{ 
    string m{"Hello\n"}; 

    string& get(double d){ 
     cout << "\tUsed :const not called...\n"; 
     return m; 
    } 
    const string& get(double d) const{ 
     cout << "\tUsed :const called...\n"; 
     return m; 
    } 
}; 

यहाँ मैं क्या अब तक ** है एक, क्योंकि मुझे पता है कि हम ऑब्जेक्ट इंटरफेस का निरीक्षण करने के लिए सी ++ लक्षणों का उपयोग कर सकते हैं: Composition: using traits to avoid forwarding functions?

** @ डेविड एस के साथ टिप्पणियों के धागे से प्रेरित यहां टोन: When should I use C++ private inheritance?

उत्तर

1

चलिए समाधान के साथ शुरू करते हैं और इसे टुकड़े से टुकड़ा समझाते हैं।

#define FORWARDING_MEMBER_FUNCTION(Inner, inner, function, qualifiers) \ 
    template< \ 
     typename... Args, \ 
     typename return_type = decltype(std::declval<Inner qualifiers>().function(std::declval<Args &&>()...)) \ 
    > \ 
    constexpr decltype(auto) function(Args && ... args) qualifiers noexcept(\ 
     noexcept(std::declval<Inner qualifiers>().function(std::forward<Args>(args)...)) and \ 
     (\ 
      std::is_reference<return_type>::value or \ 
      std::is_nothrow_move_constructible<return_type>::value \ 
     ) \ 
    ) { \ 
     return static_cast<Inner qualifiers>(inner).function(std::forward<Args>(args)...); \ 
    } 

#define FORWARDING_MEMBER_FUNCTIONS_CV(Inner, inner, function, reference) \ 
    FORWARDING_MEMBER_FUNCTION(Inner, inner, function, reference) \ 
    FORWARDING_MEMBER_FUNCTION(Inner, inner, function, const reference) \ 
    FORWARDING_MEMBER_FUNCTION(Inner, inner, function, volatile reference) \ 
    FORWARDING_MEMBER_FUNCTION(Inner, inner, function, const volatile reference) 

#define FORWARDING_MEMBER_FUNCTIONS(Inner, inner, function) \ 
    FORWARDING_MEMBER_FUNCTIONS_CV(Inner, inner, function, &) \ 
    FORWARDING_MEMBER_FUNCTIONS_CV(Inner, inner, function, &&) 

इनर उस ऑब्जेक्ट के प्रकार का प्रतिनिधित्व करता है जिसे आप अग्रेषित कर रहे हैं, और आंतरिक इसके नाम का प्रतिनिधित्व करता है। क्वालिफायर कॉन्स, अस्थिर, &, और & & का संयोजन है जो आपको अपने सदस्य फ़ंक्शन पर चाहिए।

नोएक्ससेप्ट विनिर्देश आश्चर्यजनक रूप से जटिल है क्योंकि आपको फ़ंक्शन कॉल को संभालने और साथ ही वापसी मूल्य बनाने की आवश्यकता है। यदि आप जिस फ़ंक्शन को अग्रेषित कर रहे हैं वह एक संदर्भ देता है, तो आप जानते हैं कि यह सुरक्षित है (संदर्भ हमेशा एक ही प्रकार से रचनात्मक नहीं हैं), लेकिन यदि फ़ंक्शन मूल्य से लौटाया जाता है, तो आपको यह सुनिश्चित करना होगा कि ऑब्जेक्ट का चाल कन्स्ट्रक्टर अस्वीकार्य है।

हम डिफॉल्ट टेम्पलेट तर्क return_type का उपयोग करके इसे थोड़ा सा सरल बनाने में सक्षम थे, अन्यथा हमें उस वापसी प्रकार को दो बार स्पेल करना होगा।

हम फ़ंक्शन के बॉडी में static_cast का उपयोग संक्रमित प्रकार के लिए सीवी और संदर्भ क्वालीफायर को सही तरीके से जोड़ने के लिए करते हैं। यह फ़ंक्शन पर संदर्भ क्वालीफायर द्वारा स्वचालित रूप से नहीं उठाया जाता है।

रचना

निजी वंशानुक्रम का उपयोग करने के बजाय विरासत का उपयोग करना, समाधान अधिक इस तरह दिखता है:

struct Outer : private Inner { 
    using Inner::f; 
}; 

यह

  • का लाभ पठनीयता
  • है तेज़ संकलन समय
  • डिबग में
  • तेज़ कोड (इनलाइन करने के लिए कुछ भी नहीं है) बनाता है
  • अपने constexpr प्रत्यावर्तन गहराई
  • अपने टेम्पलेट इन्स्टेन्शियशन गहराई
  • मूल्य
  • अग्रेषण के साथ कार्य करना द्वारा गैर जंगम प्रकार लौटने के साथ कार्य करना अप का उपयोग नहीं अप का उपयोग नहीं रचनाकारों के लिए
+0

आपके बहुत अच्छे उत्तर के लिए धन्यवाद! मैंने मेटा प्रोग्रामिंग के बारे में बहुत कुछ सीखा। क्या हम इसे इस तरह इस्तेमाल कर सकते हैं? 'FORWARDING_MEMBER_FUNCTIONS (std :: decay :: प्रकार, आंतरिक, फ़ंक्शन)' और सदस्य सूचक के साथ: 'FORWARDING_MEMBER_FUNCTIONS (std :: decay :: type, * ptr , फ़ंक्शन); ' ? –

+0

बीटीडब्लू, सी ++ को समान वाक्यविन्यास 'आंतरिक उपयोग करने के लिए' का उपयोग करने से रोकता है; संरचना के लिए? –

+0

@ जुलिएन__ आप पैरामीटर के प्रकार को स्वीकार करने के लिए मैक्रो बदल सकते हैं। नकारात्मकता यह है कि आपके पास FORWARDING_MEMBER_FUNCTIONS को आमंत्रित करने से पहले आपकी परिवर्तनीय घोषणा होनी चाहिए, जबकि यदि आप स्पष्ट रूप से प्रकार निर्दिष्ट करते हैं, तो वह प्रतिबंध मौजूद नहीं है। जो आपके लिए बेहतर है आप पर निर्भर है। संदर्भ क्वालीफायर के कारण, शायद आप पॉइंटर्स के साथ इसका उपयोग नहीं करना चाहेंगे। सी ++ सदस्यों के कार्यों के लिए आगे बढ़ने के लिए उपयोग वाक्यविन्यास का उपयोग करने से रोकता है, सिवाय इसके कि आपको समिति को इसे स्वीकार करने के लिए मनाने की आवश्यकता होगी। –

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