2014-12-24 10 views
11

में मेरा अपना स्मार्ट सूचक कार्यान्वयन है और अब मैं सदस्य के फ़ंक्शन को अपने सूचक द्वारा कॉल करने की समस्या को हल करने का प्रयास कर रहा हूं। मैं कोई भी() - जैसे फ़ंक्शन प्रदान नहीं करता (वास्तव में, मैं एक ऑपरेटर-> प्रदान करता हूं, लेकिन मैं इस उद्देश्य के लिए इसका उपयोग नहीं करना चाहता)।ओवरलोडिंग ऑपरेटर -> * सी ++

मेरा प्रश्न है: operator->* के हस्ताक्षर और वापसी प्रकार को कैसा दिखना चाहिए?

+9

@OP के लिए: [यहां एक पेपर] (http://aristeia.com/Papers/DDJ_Oct_1999.pdf) जो ** ** ** जो आप चाहते हैं उसका वर्णन करता है। –

+2

"सूचक-से-सदस्य ऑपरेटरों '-> *' और '। *' "को धारा 5.5 में सी ++ 11 मानक में वर्णित किया गया है। वे सी ++ में मौजूद हैं, भले ही उनका उपयोग आमतौर पर नहीं किया जाता है। –

+0

सी ++ एफएक्यू के माध्यम से सदस्य के लिए सूचक: http://www.parashift.com/c++-faq/dotstar-vs-arrowstar.html –

उत्तर

6

पूर्णता के लिए, यहाँ एक पूर्ण, compilable, कम से कम उदाहरण है, भारी आदेश प्राप्त करने के लिए इस paper I've linked to से प्रेरित है, और एक छोटे से उपयोग डेमो के साथ नीचे छीन लिया तो आप इस के साथ शुरू किया:

#include <memory> 
#include <iostream> 
#include <utility> 


// Our example class on which we'll be calling our member function pointer (MFP) 
struct Foo { 
    int bar() { 
     return 1337; 
    } 
}; 

// Return value of operator->* that represents a pending member function call 
template<typename C, typename MFP> 
struct PMFC { 
    const std::unique_ptr<C> &ptr; 
    MFP pmf; 
    PMFC(const std::unique_ptr<C> &pPtr, MFP pPmf) : ptr(pPtr), pmf(pPmf) {} 

    // the 'decltype' expression evaluates to the return type of ((C*)->*)pmf 
    decltype((std::declval<C &>().*pmf)()) operator()() { 
     return (ptr.get()->*pmf)(); 
    } 
}; 

// The actual operator definition is now trivial 
template<typename C, typename MFP> 
PMFC<C, MFP> operator->*(const std::unique_ptr<C> &ptr, MFP pmf) 
{ 
    return PMFC<C, MFP>(ptr, pmf); 
} 

// And here's how you use it 
int main() 
{ 
    std::unique_ptr<Foo> pObj(new Foo); 
    auto (Foo::*pFn)() = &Foo::bar; 
    std::cout << (pObj->*pFn)() << std::endl; 
} 
1

ओवरलोडेड ऑपरेटर ->* के लिए दो तर्क 1 होना चाहिए। 2. अपनी कक्षा का ऑब्जेक्ट, और 2. सदस्य को सूचक। सबसे सामान्य स्थिति में इसका मतलब है कि अतिभारित ऑपरेटर अपनी कक्षा प्रकार सूचक करने वाली सदस्य में से एक तर्क को स्वीकार करने का सदस्य होना चाहिए, ताकि पैरामीटर के बिना सदस्य कार्य करने के लिए एक सूचक के लिए उदाहरण के लिए यह होगा:

TYPE operator->*(void (YourClass::*mp)()); 

वापसी का प्रकार कॉल करने योग्य होना चाहिए (इस अर्थ में कि operator() उस पर लागू होता है)। यह एक और वर्ग के साथ दिखाना सबसे आसान है - यहां आपके पास एक पूर्ण उदाहरण है:

struct Caller { 
void operator()() { cout << "caller"; } 
}; 

struct A { 
void f() { cout << "function f"; } 
Caller operator->*(void (A::*mp)()) { return Caller(); } 
}; 

int main() { 
A a; 
void (A::*mp)() = &A::f; 
(a->*mp)(); 
return 0; 
} 

जो "कॉलर" आउटपुट करता है। वास्तविक दुनिया में आपको विभिन्न सूचक-से-सदस्य प्रकारों का समर्थन करने के लिए टेम्पलेट का उपयोग करना होगा। अधिक जानकारी आप Scott Meyer's paper में पा सकते हैं।

2

operator->*() लेता है दो तर्क:

  1. वह ऑब्जेक्ट जिस पर चल रहा है।
  2. सदस्य सूचक लागू किया जाना चाहिए।

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

मुझे एहसास है कि मूल प्रश्न सी ++ 03 के साथ टैग किया गया है लेकिन "उचित" सी ++ 03 कार्यान्वयन करना एक लंबा लंबा टाइपिंग व्यायाम है: आपको कोड में विविध टेम्पलेट्स द्वारा आसानी से किया जाना होगा प्रत्येक तर्क के लिए नीचे। इसलिए, यह कोड मुख्य रूप से अधिक स्पष्ट रूप से दिखाने के लिए और टाइपिंग अभ्यास के माध्यम से चलने से बचने के लिए सी ++ 11 का उपयोग करता है।

यहाँ एक सरल "स्मार्ट" सूचक operator->*() को परिभाषित है:

template <typename T> 
class my_ptr 
{ 
    T* ptr; 
public: 
    my_ptr(T* ptr): ptr(ptr) {} 

    template <typename R> 
    R& operator->*(R T::*mem) { return (this->ptr)->*mem; } 

    template <typename R, typename... Args> 
    struct callable; 
    template <typename R, typename... Args> 
    callable<R, Args...> operator->*(R (T::*mem)(Args...)); 
}; 

मैं "उचित" के लिए लगता है का समर्थन यह भी const संस्करणों को परिभाषित करने की जरूरत है: कि काफी सीधे आगे तो मैं इन को छोड़ते हुए कर रहा हूँ होना चाहिए। मूल रूप से दो संस्करण हैं:

  1. एक संस्करण एक गैर-फ़ंक्शन सदस्य को पॉइंटर ले रहा है जो केवल दिए गए सूचक के लिए संदर्भित सदस्य को लौटाता है।
  2. एक संस्करण एक फ़ंक्शन सदस्य को पॉइंटर ले रहा है जो उपयुक्त callable ऑब्जेक्ट देता है।callable में फ़ंक्शन कॉल ऑपरेटर होना आवश्यक है और इसे उचित रूप से लागू करना होगा।

तो, परिभाषित करने के लिए अगली बात callable प्रकार है: यह वस्तु के लिए एक सूचक और सदस्य के लिए एक सूचक पकड़ और उन्हें कॉल पर लागू होंगे:

#include <utility> 
template <typename T> 
    template <typename R, typename... Args> 
struct my_ptr<T>::callable { 
    T* ptr; 
    R (T::*mem)(Args...); 
    callable(T* ptr, R (T::*mem)(Args...)): ptr(ptr), mem(mem) {} 

    template <typename... A> 
    R operator()(A... args) const { 
     return (this->ptr->*this->mem)(std::forward<A>(args)...); 
    } 
}; 

खैर, कि काफी सीधे है आगे। एक मुश्किल बिट यह तथ्य है कि फंक्शन कॉल ऑपरेटर के तर्कों को तर्क दिया जा रहा है, जो सदस्य के सूचक के मुकाबले अलग-अलग प्रकार के हो सकते हैं। ऊपर दिया गया कोड बस उन्हें अग्रेषित करके स्थिति से संबंधित है।

template <typename T> 
    template <typename R, typename... Args> 
my_ptr<T>::callable<R, Args...> my_ptr<T>::operator->*(R (T::*mem)(Args...)) { 
    return callable<R, Args...>(this->ptr, mem); 
} 

ठीक है, बस इतना ही है:

लापता सा ऊपर callable प्रकार के लिए कारखाने समारोह है! फैंसी सी ++ 11 वैरिएडिक टेम्पलेट्स का उपयोग करके यह काफी कोड है। एक सी ++ 03 को खिलाने के लिए इस सामान को टाइप करना वास्तव में कुछ नहीं है जो मैं चाहता हूं। प्लस तरफ, मैं सोचता हूं ये ऑपरेटर गैर-सदस्य कार्य हो सकते हैं। यही है, उन्हें उपयुक्त नामस्थान में कार्यान्वित किया जा सकता है जिसमें केवल इन ऑपरेटरों का उपयोग होता है और एक खाली टैग-प्रकार होता है जो एक स्मार्ट सूचक प्रकार से प्राप्त होता है। टैग-टैग आधार होने के साथ ऑपरेटर एडीएल के माध्यम से पाए जाएंगे और सभी स्मार्ट पॉइंटर के लिए काम करेंगे। callable बनाने के लिए आवश्यक पॉइंटर को पकड़ने के लिए वे operator->() का उपयोग कर सकते हैं।

सी ++ 11 का उपयोग करना यह किसी भी विशिष्ट स्मार्ट सूचक प्रकार से स्वतंत्र operator->*() के लिए समर्थन को लागू करने के लिए वास्तव में सीधे आगे है। नीचे दिया गया कोड कार्यान्वयन और सरल उपयोग दिखाता है। यह इस तथ्य का उपयोग करता है कि यह संस्करण केवल एडीएल के आधार पर पाया जा सकता है (आपको इसके लिए कोई निर्देश या निर्देश नहीं होना चाहिए) और स्मार्ट पॉइंटर्स शायद operator->() लागू करते हैं: कोड स्मार्ट पॉइंटर के पॉइंटर को पकड़ने के लिए इस फ़ंक्शन का उपयोग करता है । member_access नामस्थान शायद अन्य स्मार्ट पॉइंटर्स द्वारा बस एक उपयुक्त शीर्षलेख में जाना चाहिए जो तब member_access::member_acccess_tag (जो private (!) बेस क्लास हो सकता है क्योंकि यह अभी भी member_access में देखने के लिए एडीएल को ट्रिगर करता है)।

#include <utility> 

namespace member_access 
{ 
    struct member_access_tag {}; 

    template <typename Ptr, typename R, typename T> 
    R& operator->*(Ptr ptr, R T::*mem) { 
     return ptr.operator->()->*mem; 
    } 

    template <typename R, typename T, typename... Args> 
    struct callable { 
     T* ptr; 
     R (T::*mem)(Args...); 
     callable(T* ptr, R (T::*mem)(Args...)): ptr(ptr), mem(mem) {} 

     template <typename... A> 
     R operator()(A... args) const { 
      return (this->ptr->*this->mem)(std::forward<A>(args)...); 
     } 
    }; 

    template <typename Ptr, typename R, typename T, typename... Args> 
    callable<R, T, Args...> operator->*(Ptr ptr, R (T::*mem)(Args...)) { 
     return callable<R, T, Args...>(ptr.operator->(), mem); 
    } 
} 

template <typename T> 
class my_ptr 
    : private member_access::member_access_tag 
{ 
    T* ptr; 
public: 
    my_ptr(T* ptr): ptr(ptr) {} 
    T* operator->() { return this->ptr; } 
}; 
संबंधित मुद्दे