मैं एक सदस्य फ़ंक्शन कैसे पास कर सकता हूं जहां एक मुफ्त फ़ंक्शन अपेक्षित है?


122

प्रश्न निम्नलिखित है: कोड के इस टुकड़े पर विचार करें:

#include <iostream>


class aClass
{
public:
    void aTest(int a, int b)
    {
        printf("%d + %d = %d", a, b, a + b);
    }
};

void function1(void (*function)(int, int))
{
    function(1, 1);
}

void test(int a,int b)
{
    printf("%d - %d = %d", a , b , a - b);
}

int main (int argc, const char* argv[])
{
    aClass a();

    function1(&test);
    function1(&aClass::aTest); // <-- How should I point to a's aClass::test function?

    return 0;
}

मैं कैसे उपयोग कर सकते हैं aके aClass::testलिए एक तर्क के रूप function1? मैं ऐसा करने में फंस गया हूं।

मैं कक्षा के एक सदस्य का उपयोग करना चाहूंगा।


1
इस उत्तर पर एक नज़र डालें stackoverflow.com/questions/2402579/… और साथ ही यह C ++ FAQ parashift.com/c++-faq/pointers-to-members.html
amdn

16
यह बिल्कुल डुप्लिकेट नहीं है (कम से कम उस विशेष प्रश्न से नहीं जो जुड़ा हुआ है)। यह प्रश्न इस बात के बारे में है कि एक सदस्य को कैसे घोषित किया जाए जो एक फ़ंक्शन का सूचक है; यह एक गैर-स्थिर सदस्य फ़ंक्शन के लिए एक पैरामीटर के रूप में एक पॉइंटर पास करने के तरीके के बारे में है।
CarLuva

जवाबों:


144

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

void (aClass::*)(int, int)

उस प्रकार के बजाय जिसका आप उपयोग करने का प्रयास करते हैं

void (*)(int, int)

एक दृष्टिकोण सदस्य कार्य करने staticमें शामिल हो सकता है जिस स्थिति में इसे किसी भी वस्तु को कॉल करने की आवश्यकता नहीं होती है और आप इसे प्रकार के साथ उपयोग कर सकते हैं void (*)(int, int)

यदि आपको अपनी कक्षा के किसी भी गैर-स्थैतिक सदस्य तक पहुँचने की आवश्यकता है और आपको फ़ंक्शन पॉइंटर्स के साथ चिपके रहने की आवश्यकता है, उदाहरण के लिए, क्योंकि फ़ंक्शन सी इंटरफ़ेस का हिस्सा है, तो आपका सबसे अच्छा विकल्प void*फ़ंक्शन फ़ंक्शन और कॉल करने के लिए हमेशा अपने फ़ंक्शन को पास करना है। एक अग्रेषण फ़ंक्शन के माध्यम से आपका सदस्य जो किसी वस्तु को प्राप्त करता हैvoid* और फिर सदस्य फ़ंक्शन को कॉल करता है।

एक उचित C ++ इंटरफ़ेस में, आप चाहते हैं कि आपके फ़ंक्शन को फ़ंक्शन ऑब्जेक्ट्स के लिए मनमाना वर्ग प्रकारों का उपयोग करने के लिए अस्थायी तर्क ले सकें। यदि टेम्प्लेटेड इंटरफ़ेस का उपयोग करना अवांछनीय है, तो आपको कुछ इस तरह का उपयोग करना चाहिए std::function<void(int, int)>: आप इन के लिए एक उपयुक्त कॉल करने योग्य फ़ंक्शन ऑब्जेक्ट बना सकते हैं, जैसे, उपयोग करना std::bind()

वर्ग प्रकार या उपयुक्त के लिए टेम्प्लेट तर्क का उपयोग करने वाले प्रकार-सुरक्षित दृष्टिकोण एक std::function<...>का उपयोग करने की तुलना में बेहतर हैंvoid* इंटरफ़ेस क्योंकि वे गलत प्रकार के कलाकारों के कारण त्रुटियों की क्षमता को दूर करते हैं।

सदस्य फ़ंक्शन को कॉल करने के लिए फ़ंक्शन पॉइंटर का उपयोग कैसे करें, यह स्पष्ट करने के लिए, यहां एक उदाहरण है:

// the function using the function pointers:
void somefunction(void (*fptr)(void*, int, int), void* context) {
    fptr(context, 17, 42);
}

void non_member(void*, int i0, int i1) {
    std::cout << "I don't need any context! i0=" << i0 << " i1=" << i1 << "\n";
}

struct foo {
    void member(int i0, int i1) {
        std::cout << "member function: this=" << this << " i0=" << i0 << " i1=" << i1 << "\n";
    }
};

void forwarder(void* context, int i0, int i1) {
    static_cast<foo*>(context)->member(i0, i1);
}

int main() {
    somefunction(&non_member, 0);
    foo object;
    somefunction(&forwarder, &object);
}

ठीक है, मुझे यह जवाब पसंद है! क्या आप कृपया निर्दिष्ट कर सकते हैं कि "अपने सदस्य को एक अग्रेषण फ़ंक्शन के माध्यम से कॉल करें जो शून्य से एक ऑब्जेक्ट प्राप्त करता है * और फिर सदस्य फ़ंक्शन को कॉल करता है" या इसके लिए एक उपयोगी लिंक साझा करें? धन्यवाद
जॉर्ज लीइटाओ

मैने सोचा कि मैंने पा लिया। (मैंने आपकी पोस्ट संपादित की है) स्पष्टीकरण और उदाहरण के लिए धन्यवाद, वास्तव में उपयोगी। बस पुष्टि करने के लिए: प्रत्येक सदस्य फ़ंक्शन के लिए जिसे मैं इंगित करना चाहता हूं, मुझे एक फारवर्डर बनाना होगा। सही?
जॉर्ज लेइताओ सेप

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

1
मैं इस जवाब को नापसंद करता हूं क्योंकि यह उपयोग करता है void*, जिसका अर्थ है कि आप बहुत बुरा कीड़े प्राप्त कर सकते हैं, क्योंकि यह अब टाइप नहीं किया गया है।
सुपरलुकस

@Superlokkus: आप कृपया हमें एक विकल्प के साथ बता सकते हैं?
डिटमार कुल्हड़

88

@ पे बेकर का जवाब ठीक है, लेकिन आप इसे C ++ 11 में classएक स्पष्ट पैरामीटर के रूप में उदाहरण दिए बिना भी कर सकते हैं function1:

#include <functional>
using namespace std::placeholders;

void function1(std::function<void(int, int)> fun)
{
    fun(1, 1);
}

int main (int argc, const char * argv[])
{
   ...

   aClass a;
   auto fp = std::bind(&aClass::test, a, _1, _2);
   function1(fp);

   return 0;
}

1
है void function1(std::function<void(int, int)>)सही?
Deqing

2
आपको फ़ंक्शन तर्क को एक चर नाम देने की आवश्यकता है और फिर चर नाम वह है जो आप वास्तव में पास करते हैं। तो: void function1(std::function<void(int, int)> functionToCall)और फिर functionToCall(1,1);। मैंने उत्तर को संपादित करने की कोशिश की, लेकिन किसी ने इसे अस्वीकार कर दिया क्योंकि किसी कारण से कोई मतलब नहीं था। हम देखेंगे कि क्या यह किसी बिंदु पर उखड़ जाता है।
डॉर्की इंजीनियर

1
@DorkyEngineer यह बहुत अजीब है, मुझे लगता है कि आपको सही होना चाहिए, लेकिन मुझे नहीं पता कि यह त्रुटि इतने लंबे समय के लिए कैसे अनजान हो सकती है। वैसे भी, मैंने अब जवाब संपादित कर दिया है।
मैट फिलिप्स

2
मैंने इस पोस्ट को यह कहते हुए पाया कि std :: function से गंभीर परफॉरमेंस पेनल्टी है।
केविन

2
@kevin, आप अपनी टिप्पणी को संशोधित करना चाह सकते हैं, क्योंकि उस पोस्ट के उत्तरों ने बेंचमार्क में एक दोष दिखाया था।
जॉर्ज लीताओ

54

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

void function1(void (aClass::*function)(int, int), aClass& a) {
    (a.*function)(1, 1);
}

और इसे कॉल करने के लिए:

aClass a; // note: no parentheses; with parentheses it's a function declaration
function1(&aClass::test, a);

1
आपका बहुत बहुत धन्यवाद। मुझे अभी पता चला है कि कोष्ठक यहाँ गिने जाते हैं: function1 (& (aClass :: test), a) MSVC2013 के साथ काम करता है लेकिन gcc के साथ नहीं। gcc को सीधे और सीधे कक्षा के नाम के सामने (जो मुझे भ्रामक लगता है, क्योंकि & ऑपरेटर फ़ंक्शन का पता लेता है, वर्ग का नहीं)
kritzel_sw

मैंने सोचा था कि functionमें void (aClass::*function)(int, int)एक प्रकार था, की वजह से उस में एक प्रकार है typedef void (aClass::*function)(int, int)
ओलुमाइड

@ ऑलमाइड - typedef int X;एक प्रकार को परिभाषित करता है; int X;एक वस्तु बनाता है।
पीट बेकर

11

2011 से, यदि आप बदल सकते हैं function1, तो ऐसा करें, जैसे:

#include <functional>
#include <cstdio>

using namespace std;

class aClass
{
public:
    void aTest(int a, int b)
    {
        printf("%d + %d = %d", a, b, a + b);
    }
};

template <typename Callable>
void function1(Callable f)
{
    f(1, 1);
}

void test(int a,int b)
{
    printf("%d - %d = %d", a , b , a - b);
}

int main()
{
    aClass obj;

    // Free function
    function1(&test);

    // Bound member function
    using namespace std::placeholders;
    function1(std::bind(&aClass::aTest, obj, _1, _2));

    // Lambda
    function1([&](int a, int b) {
        obj.aTest(a, b);
    });
}

( लाइव डेमो )

ध्यान दें कि मैंने आपकी टूटी हुई वस्तु परिभाषा ( aClass a();एक फ़ंक्शन घोषित करता है) तय की।


2

मैंने एक समान प्रश्न पूछा ( C ++ ओपनफ्रेमवर्क अन्य कक्षाओं से पासिंग शून्य ), लेकिन मुझे जो उत्तर मिला वह स्पष्ट था इसलिए भविष्य के रिकॉर्ड के लिए स्पष्टीकरण:

इसके रूप में std :: function का उपयोग करना आसान है:

 void draw(int grid, std::function<void()> element)

और फिर कॉल करें:

 grid.draw(12, std::bind(&BarrettaClass::draw, a, std::placeholders::_1));

या इससे भी आसान:

  grid.draw(12, [&]{a.draw()});

जहाँ आप एक लैम्बडा बनाते हैं, जो ऑब्जेक्ट को संदर्भ द्वारा कैप्चर करता है


1
यह देखते हुए कि यह CPP टैग किया गया है, मुझे लगता है कि यह सबसे साफ समाधान है। हम एक निरंतर संदर्भ के लिए इस्तेमाल कर सकते हैं std::functionमें drawहर कॉल पर कॉपी करने की हालांकि बजाय।
सोहैब

2
प्लेसहोल्डर को इस उदाहरण में उपयोग नहीं किया जाना चाहिए क्योंकि फ़ंक्शन किसी भी परम को नहीं लेता है।
kroiz

1

मैंने सदस्य फ़ंक्शन को स्थिर और सभी कार्यों के रूप में बनाया:

#include <iostream>

class aClass
{
public:
    static void aTest(int a, int b)
    {
        printf("%d + %d = %d\n", a, b, a + b);
    }
};

void function1(int a,int b,void function(int, int))
{
    function(a, b);
}

void test(int a,int b)
{
    printf("%d - %d = %d\n", a , b , a - b);
}

int main (int argc, const char* argv[])
{
    aClass a;

    function1(10,12,test);
    function1(10,12,a.aTest); // <-- How should I point to a's aClass::test function?

    getchar();return 0;
}

न केवल मुख्य प्रश्न के लिए हल "मैं एक aClass का उपयोग कैसे कर सकता हूं :: function1 के तर्क के रूप में परीक्षण?" लेकिन यह भी वर्ग से बाहरी कोड का उपयोग कर वर्ग निजी चर को संशोधित करने से बचने के लिए सिफारिश की है
मैथेन्जिनेर

1

यह निश्चित नहीं है कि यह अविश्वसनीय रूप से सरल समाधान क्यों पारित किया गया है:

#include <stdio.h>

class aClass
{
public:
    void aTest(int a, int b)
    {
        printf("%d + %d = %d\n", a, b, a + b);
    }
};

template<class C>
void function1(void (C::*function)(int, int), C& c)
{
    (c.*function)(1, 1);
}
void function1(void (*function)(int, int)) {
  function(1, 1);
}

void test(int a,int b)
{
    printf("%d - %d = %d\n", a , b , a - b);
}

int main (int argc, const char* argv[])
{
    aClass a;

    function1(&test);
    function1<aClass>(&aClass::aTest, a);
    return 0;
}

आउटपुट:

1 - 1 = 0
1 + 1 = 2

0

यदि आपको वास्तव में उदाहरण का उपयोग करने की आवश्यकता नहीं है a (यानी आप इसे @mathengineer के उत्तर की तरह स्थिर बना सकते हैं ) तो आप बस गैर-कैप्चर लैम्ब्डा में पास कर सकते हैं। (जो सूचक कार्य करने के लिए क्षय)


#include <iostream>

class aClass
{
public:
   void aTest(int a, int b)
   {
      printf("%d + %d = %d", a, b, a + b);
   }
};

void function1(void (*function)(int, int))
{
    function(1, 1);
}

int main()
{
   //note: you don't need the `+`
   function1(+[](int a,int b){return aClass{}.aTest(a,b);}); 
}

Wandbox


ध्यान दें: यदि aClassनिर्माण करने के लिए महंगा है या साइड इफेक्ट है, तो यह एक अच्छा तरीका नहीं हो सकता है।


-1

अब आप अपना सिर पीटना बंद कर सकते हैं। यहाँ समर्थन करने के लिए सदस्य समारोह के लिए आवरण है मौजूदा मैदान में ले जा रहा कार्यों सी तर्क के रूप में कार्य।thread_localनिर्देश यहाँ कुंजी है।

http://cpp.sh/9jhk3

// Example program
#include <iostream>
#include <string>

using namespace std;

typedef int FooCooker_ (int);

// Existing function
extern "C" void cook_10_foo (FooCooker_ FooCooker) {
    cout << "Cooking 10 Foo ..." << endl;
    cout << "FooCooker:" << endl;
    FooCooker (10);
}

struct Bar_ {
    Bar_ (int Foo = 0) : Foo (Foo) {};
    int cook (int Foo) {
        cout << "This Bar got " << this->Foo << endl;
        if (this->Foo >= Foo) {
            this->Foo -= Foo;
            cout << Foo << " cooked" << endl;
            return Foo;
        } else {
            cout << "Can't cook " <<  Foo << endl;
            return 0;
        }
    }
    int Foo = 0;
};

// Each Bar_ object and a member function need to define
// their own wrapper with a global thread_local object ptr
// to be called as a plain C function.
thread_local static Bar_* BarPtr = NULL;
static int cook_in_Bar (int Foo) {
    return BarPtr->cook (Foo);
}

thread_local static Bar_* Bar2Ptr = NULL;
static int cook_in_Bar2 (int Foo) {
    return Bar2Ptr->cook (Foo);
}

int main () {
  BarPtr = new Bar_ (20);
  cook_10_foo (cook_in_Bar);

  Bar2Ptr = new Bar_ (40);
  cook_10_foo (cook_in_Bar2);

  delete BarPtr;
  delete Bar2Ptr;
  return 0;
}

कृपया इस दृष्टिकोण के साथ किसी भी मुद्दे पर टिप्पणी करें।

अन्य उत्तर मौजूदा सादे Cकार्यों को कॉल करने में विफल होते हैं : http://cpp.sh/8exun


3
इसलिए std :: bind या एक लैम्ब्डा का उपयोग करने के बजाय आप एक वैश्विक वैरिएबल पर निर्भर उदाहरण को लपेटने के लिए। मैं अन्य उत्तरों की तुलना में इस दृष्टिकोण का कोई लाभ नहीं देख सकता।
सुपर

@ सुपर, अन्य उत्तर मौजूदा Cकार्यों को तर्क के रूप में सादे कार्यों में नहीं बुला सकते हैं ।
नेकटविट

2
सवाल यह है कि सदस्य फ़ंक्शन को कैसे कॉल किया जाए। नि: शुल्क फ़ंक्शन में पासिंग पहले से ही प्रश्न में ओपी के लिए काम कर रहा है। आप कुछ भी नहीं कर रहे हैं। यहाँ केवल हार्ड कोडित कार्य और एक वैश्विक Foo_ पॉइंटर है। यदि आप किसी भिन्न सदस्य फ़ंक्शन को कॉल करना चाहते हैं तो यह पैमाना कैसे होगा? आपको अंतर्निहित कार्यों को फिर से लिखना होगा, या प्रत्येक लक्ष्य के लिए अलग-अलग लोगों का उपयोग करना होगा।
सुपर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.