Jak mogę przekazać funkcję członkowską, w której oczekuje się wolnej funkcji?

Pytanie brzmi następująco: rozważ ten fragment kodu:

#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;
}

Jak mogę użyć a's aClass::test jako argumentu do function1? Utknąłem w tym.

Chciałbym dostać się do członka klasy.
Author: NathanOliver, 2012-09-30

5 answers

Nie ma nic złego w używaniu wskaźników funkcji. Jednak wskaźniki do niestatycznych funkcji Członkowskich nie są podobne do zwykłych wskaźników funkcji: funkcje Członkowskie muszą być wywołane NA obiekcie, który jest przekazywany jako argument niejawny do funkcji. Sygnatura funkcji członka powyżej jest, więc

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

Zamiast typu, którego próbujesz użyć

void (*)(int, int)

Jedno podejście może polegać na tym, że funkcja member static w takim przypadku nie wymaga żadnego obiektu do być wywołane i można go używać z typem void (*)(int, int).

Jeśli chcesz uzyskać dostęp do dowolnego niestatycznego członka twojej klasy i musisz trzymać się wskaźników funkcji, np. ponieważ funkcja jest częścią interfejsu C, najlepszą opcją jest zawsze przekazać void* do swojej funkcji biorąc Wskaźniki funkcji i wywołać swojego członka poprzez funkcję przekierowującą, która uzyskuje obiekt z void*, a następnie wywołuje funkcję członka.

W odpowiednim interfejsie C++ możesz chcesz przyjrzeć się, czy twoja funkcja przyjmuje argument template, aby obiekty funkcyjne używały dowolnych typów klas. Jeśli używanie szablonowego interfejsu jest niepożądane, powinieneś użyć czegoś w rodzaju std::function<void(int, int)>: możesz utworzyć dla nich obiekt funkcji, który można wywołać, np. używając std::bind().

Podejście bezpieczne dla typu używając argumentu szablonu dla typu klasy lub odpowiedniego std::function<...> jest lepsze niż używanie interfejsu void*, ponieważ usuwa potencjalne błędy z powodu rzucania do niewłaściwego Typ.

Aby wyjaśnić, jak używać wskaźnika funkcji do wywoływania funkcji członka, oto przykład:

// 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);
}
 96
Author: Dietmar Kühl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-22 14:12:39

@Pete Becker ' s answer is fine but you can also do it without passing the class instance as an explicit parameter to function1 in C++ 11:

#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;
}
 58
Author: Matt Phillips,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-15 15:45:51

Wskaźnik do funkcji członka różni się od wskaźnika do funkcji. Aby użyć funkcji członka za pomocą wskaźnika, potrzebujesz wskaźnika do niej (oczywiście ) I obiektu do zastosowania go. Więc odpowiednią wersją function1 będzie

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

I nazwać go:

aClass a; // note: no parentheses; with parentheses it's a function declaration
function1(&aClass::test, a);
 37
Author: Pete Becker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-23 16:21:34

Od 2011 roku, jeśli możesz zmienić function1, Zrób to tak:

#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);
    });
}

(live demo )

Zauważ również, że naprawiłem Twoją zepsutą definicję obiektu (aClass a(); deklaruje funkcję).

 0
Author: Lightness Races in Orbit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-10 16:03:35

Możecie przestać walić się w głowy. Oto wrapper dla funkcji member obsługującej istniejące funkcjeprzyjmujące jako argumenty zwykłe funkcje C. thread_local dyrektywa jest tutaj kluczem.

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;
}

Proszę skomentować wszelkie problemy z tym podejściem.

Inne odpowiedzi nie wywołują istniejących zwykłych C funkcji: http://cpp.sh/8exun

 0
Author: neckTwi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-11 13:59:34