समारोह सूचक सदस्य समारोह के लिए


89

मैं एक क्लास के एक सदस्य के रूप में एक फ़ंक्शन पॉइंटर सेट करना चाहता हूं जो उसी क्लास में दूसरे फ़ंक्शन के लिए एक पॉइंटर है। मैं ऐसा क्यों कर रहा हूं इसके कारण जटिल हैं।

इस उदाहरण में, मैं चाहूंगा कि आउटपुट "1" हो

class A {
public:
 int f();
 int (*x)();
}

int A::f() {
 return 1;
}


int main() {
 A a;
 a.x = a.f;
 printf("%d\n",a.x())
}

लेकिन यह संकलन में विफल रहता है। क्यों?



@jww और उस प्रश्न में CiroSantilli के उत्तर की जाँच करें, अन्य उत्तर कमोबेश विषय से अधिक हैं। असल में, बस int (C :: * function_pointer_var) (int) = & C :: विधि; फिर सी ग; और (c। * function_pointer_var) (2)।
jw_

जवाबों:


157

वाक्य विन्यास गलत है। एक सदस्य सूचक एक साधारण सूचक से एक अलग प्रकार की श्रेणी है। सदस्य सूचक को अपनी कक्षा की एक वस्तु के साथ उपयोग करना होगा:

class A {
public:
 int f();
 int (A::*x)(); // <- declare by saying what class it is a pointer to
};

int A::f() {
 return 1;
}


int main() {
 A a;
 a.x = &A::f; // use the :: syntax
 printf("%d\n",(a.*(a.x))()); // use together with an object of its class
}

a.xफ़ंक्शन को कॉल करने के लिए किस ऑब्जेक्ट पर अभी तक नहीं कहा गया है। यह सिर्फ यह कहता है कि आप ऑब्जेक्ट में संग्रहीत पॉइंटर का उपयोग करना चाहते हैं a। ऑपरेटर aको बाएं .*ऑपरेटर के रूप में एक और समय की तैयारी करते हुए संकलक को बताएगा कि फ़ंक्शन को किस ऑब्जेक्ट पर कॉल करना है।


मुझे पता है कि यह पुराना है, लेकिन मुझे समझ में नहीं आता कि यह काम (a.*a.x)()क्यों (a.*x)()नहीं करता है?
गौरव सहगल

3
@gau क्योंकि x स्कोप में नहीं है
जोहान्स शाउब -

13
मुझे हर बार इसका इस्तेमाल करना है। वाक्य विन्यास भ्रामक है, लेकिन इसका अर्थ यह है कि यदि आप इसे तोड़ते हैं। a.xकक्षा ए के एक सदस्य समारोह के लिए एक *a.xसूचक है सूचक तो अब यह एक फ़ंक्शन संदर्भ है। a.(*a.x)एक उदाहरण के लिए "बाइंड्स" फ़ंक्शन (जैसे a.f)। (a.(*a.x))इस जटिल सिंटैक्स को समूहित करना आवश्यक है, और (a.(*a.x))()वास्तव में इस पद्धति को aबिना किसी तर्क के लागू करता है।
jwm

23

int (*x)()सदस्य कार्य के लिए एक सूचक नहीं है। एक पॉइंटर टू मेम्बर फंक्शन इस तरह लिखा जाता है int (A::*x)(void) = &A::f;:।


17

स्ट्रिंग कमांड पर सदस्य फ़ंक्शन को कॉल करें

#include <iostream>
#include <string>


class A 
{
public: 
    void call();
private:
    void printH();
    void command(std::string a, std::string b, void (A::*func)());
};

void A::printH()
{
    std::cout<< "H\n";
}

void A::call()
{
    command("a","a", &A::printH);
}

void A::command(std::string a, std::string b, void (A::*func)())
{
    if(a == b)
    {
        (this->*func)();
    }
}

int main()
{
    A a;
    a.call();
    return 0;
}

(this->*func)();क्लास के नाम के साथ फ़ंक्शन पॉइंटर घोषित करने के तरीके पर ध्यान देंvoid (A::*func)()


11

आपको एक फ़ंक्शन के लिए एक पॉइंटर का उपयोग करने की आवश्यकता है, न कि केवल एक फ़ंक्शन के लिए एक पॉइंटर।

class A { 
    int f() { return 1; }
public:
    int (A::*x)();

    A() : x(&A::f) {}
};

int main() { 
   A a;
   std::cout << (a.*a.x)();
   return 0;
}

3

हालांकि यह इस पृष्ठ पर कहीं और स्टर्लिंग के उत्तरों पर आधारित है, मेरे पास एक उपयोग का मामला था जो उनके द्वारा पूरी तरह से हल नहीं किया गया था; निम्नलिखित कार्य करने के लिए संकेत के एक वेक्टर के लिए:

#include <iostream>
#include <vector>
#include <stdio.h>
#include <stdlib.h>

class A{
public:
  typedef vector<int> (A::*AFunc)(int I1,int I2);
  vector<AFunc> FuncList;
  inline int Subtract(int I1,int I2){return I1-I2;};
  inline int Add(int I1,int I2){return I1+I2;};
  ...
  void Populate();
  void ExecuteAll();
};

void A::Populate(){
    FuncList.push_back(&A::Subtract);
    FuncList.push_back(&A::Add);
    ...
}

void A::ExecuteAll(){
  int In1=1,In2=2,Out=0;
  for(size_t FuncId=0;FuncId<FuncList.size();FuncId++){
    Out=(this->*FuncList[FuncId])(In1,In2);
    printf("Function %ld output %d\n",FuncId,Out);
  }
}

int main(){
  A Demo;
  Demo.Populate();
  Demo.ExecuteAll();
  return 0;
}

ऐसा कुछ उपयोगी है यदि आप अनुक्रमित कार्यों के साथ एक कमांड दुभाषिया लिख ​​रहे हैं जिसे पैरामीटर सिंटैक्स के साथ शादी करने और सुझावों आदि की मदद करने की आवश्यकता है। संभवतः मेनू में भी उपयोगी है।


1
जैसा कि परिभाषित किया गया है, AFunc सदस्य फ़ंक्शन के लिए एक संकेतक है जो दो ints ले रहा है और एक ints का वेक्टर वापस कर रहा है । लेकिन सदस्यों ने int लौटने का इशारा किया , है ना? मुझे लगता है कि typedef int (A::*AFunc)(int I1,int I2);
टाइपडिफ

2

जब आप दुर्भाग्य से एक मौजूदा सदस्य फ़ंक्शन पॉइंटर को एक सादे फ़ंक्शन पॉइंटर में नहीं बदल सकते हैं, तो आप एक एडेप्टर फ़ंक्शन टेम्प्लेट को काफी सरल तरीके से बना सकते हैं, जो इस तरह के एक सामान्य फ़ंक्शन में संकलन-समय पर ज्ञात सदस्य फ़ंक्शन पॉइंटर को लपेटता है:

template <class Type>
struct member_function;

template <class Type, class Ret, class... Args>
struct member_function<Ret(Type::*)(Args...)>
{
    template <Ret(Type::*Func)(Args...)>
    static Ret adapter(Type &obj, Args&&... args)
    {
        return (obj.*Func)(std::forward<Args>(args)...);
    }
};

template <class Type, class Ret, class... Args>
struct member_function<Ret(Type::*)(Args...) const>
{
    template <Ret(Type::*Func)(Args...) const>
    static Ret adapter(const Type &obj, Args&&... args)
    {
        return (obj.*Func)(std::forward<Args>(args)...);
    }
};

 

int (*func)(A&) = &member_function<decltype(&A::f)>::adapter<&A::f>;

ध्यान दें कि सदस्य फ़ंक्शन को कॉल करने के लिए, एक उदाहरण प्रदान Aकरना होगा।


आपने मुझे, @ IllidanS4 प्रेरित किया है। मेरा जवाब देखिए। +1
मेमथा

1

@ IllidanS4 के उत्तर पर बिल्डिंग, मैंने एक टेम्प्लेट क्लास बनाई है, जो पूर्वनिर्धारित तर्कों और क्लास इंस्टेंस के साथ वस्तुतः किसी भी सदस्य को बाद में कॉल करने के लिए संदर्भ द्वारा पारित करने की अनुमति देता है।



template<class RET, class... RArgs> class Callback_t {
public:
    virtual RET call(RArgs&&... rargs) = 0;
    //virtual RET call() = 0;
};

template<class T, class RET, class... RArgs> class CallbackCalltimeArgs : public Callback_t<RET, RArgs...> {
public:
    T * owner;
    RET(T::*x)(RArgs...);
    RET call(RArgs&&... rargs) {
        return (*owner.*(x))(std::forward<RArgs>(rargs)...);
    };
    CallbackCalltimeArgs(T* t, RET(T::*x)(RArgs...)) : owner(t), x(x) {}
};

template<class T, class RET, class... Args> class CallbackCreattimeArgs : public Callback_t<RET> {
public:
    T* owner;
    RET(T::*x)(Args...);
    RET call() {
        return (*owner.*(x))(std::get<Args&&>(args)...);
    };
    std::tuple<Args&&...> args;
    CallbackCreattimeArgs(T* t, RET(T::*x)(Args...), Args&&... args) : owner(t), x(x),
        args(std::tuple<Args&&...>(std::forward<Args>(args)...)) {}
};

परीक्षण / उदाहरण:

class container {
public:
    static void printFrom(container* c) { c->print(); };
    container(int data) : data(data) {};
    ~container() {};
    void print() { printf("%d\n", data); };
    void printTo(FILE* f) { fprintf(f, "%d\n", data); };
    void printWith(int arg) { printf("%d:%d\n", data, arg); };
private:
    int data;
};

int main() {
    container c1(1), c2(20);
    CallbackCreattimeArgs<container, void> f1(&c1, &container::print);
    Callback_t<void>* fp1 = &f1;
    fp1->call();//1
    CallbackCreattimeArgs<container, void, FILE*> f2(&c2, &container::printTo, stdout);
    Callback_t<void>* fp2 = &f2;
    fp2->call();//20
    CallbackCalltimeArgs<container, void, int> f3(&c2, &container::printWith);
    Callback_t<void, int>* fp3 = &f3;
    fp3->call(15);//20:15
}

जाहिर है, यह तभी काम करेगा जब दिए गए तर्क और मालिक वर्ग अभी भी मान्य हैं। जहाँ तक पठनीयता है ... कृपया मुझे क्षमा करें।

संपादित करें: टुपल को सामान्य भंडारण करके अनावश्यक मॉलोक को हटा दिया गया। संदर्भ के लिए विरासत में मिला प्रकार। इसके बजाय कॉलटाइम पर सभी तर्क प्रदान करने का विकल्प जोड़ा गया। अब दोनों होने पर काम ...।

संपादन 2: जैसा कि वादा किया गया था, दोनों। केवल प्रतिबंध (जो मैं देखता हूं) यह है कि कॉलबैक फ़ंक्शन में रनटाइम आपूर्ति किए गए तर्कों से पहले पूर्वनिर्धारित तर्क आने चाहिए। Gcc अनुपालन में मदद के लिए @Chipster का धन्यवाद। यह विंडोज पर ubuntu और विजुअल स्टूडियो पर gcc पर काम करता है।

#ifdef _WIN32
#define wintypename typename
#else
#define wintypename
#endif

template<class RET, class... RArgs> class Callback_t {
public:
    virtual RET call(RArgs... rargs) = 0;
    virtual ~Callback_t() = default;
};

template<class RET, class... RArgs> class CallbackFactory {
private:
    template<class T, class... CArgs> class Callback : public Callback_t<RET, RArgs...> {
    private:
        T * owner;
        RET(T::*x)(CArgs..., RArgs...);
        std::tuple<CArgs...> cargs;
        RET call(RArgs... rargs) {
            return (*owner.*(x))(std::get<CArgs>(cargs)..., rargs...);
        };
    public:
        Callback(T* t, RET(T::*x)(CArgs..., RArgs...), CArgs... pda);
        ~Callback() {};
    };
public:
    template<class U, class... CArgs> static Callback_t<RET, RArgs...>* make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...));
};
template<class RET2, class... RArgs2> template<class T2, class... CArgs2> CallbackFactory<RET2, RArgs2...>::Callback<T2, CArgs2...>::Callback(T2* t, RET2(T2::*x)(CArgs2..., RArgs2...), CArgs2... pda) : x(x), owner(t), cargs(std::forward<CArgs2>(pda)...) {}
template<class RET, class... RArgs> template<class U, class... CArgs> Callback_t<RET, RArgs...>* CallbackFactory<RET, RArgs...>::make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...)) {
    return new wintypename CallbackFactory<RET, RArgs...>::Callback<U, CArgs...>(owner, func, std::forward<CArgs>(cargs)...);
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.