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


Pytanie brzmi: 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 argument do function1? Utknąłem w tym.

Chciałbym uzyskać dostęp do członka klasy.


76
2017-09-30 16:28


pochodzenie


Spójrz na tę odpowiedź stackoverflow.com/questions/2402579/...a także ten C ++ FAQ parashift.com/c++-faq/pointers-to-members.html - amdn
To absolutnie nie jest duplikat (a przynajmniej nie zadającego się konkretnego pytania). To pytanie dotyczy sposobu deklarowania członka, który jest wskaźnikiem funkcji; chodzi o to, jak przekazać wskaźnik do niestatycznej funkcji składowej jako parametru. - CarLuva


Odpowiedzi:


Nie ma nic złego w używaniu wskaźników funkcji. Jednak wskaźniki do niestatycznych funkcji składowych nie są podobne do normalnych wskaźników funkcji: funkcje składowe muszą być wywołane na obiekcie, który jest przekazywany jako niejawny argument do funkcji. Podpis powyższej funkcji członka jest zatem

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

raczej niż typ, którego próbujesz użyć

void (*)(int, int)

Jedno podejście może polegać na wykonywaniu funkcji członka static w takim przypadku nie wymaga on żadnego obiektu i można go używać z typem void (*)(int, int).

Jeśli potrzebujesz dostępu do niestatycznego członka twojej klasy i musisz trzymać się wskaźników funkcji, np. ponieważ funkcja jest częścią interfejsu C, twoja najlepsza opcja to zawsze przekazywać a void* do funkcji przyjmowania wskaźników funkcyjnych i wywoływania swojego członka poprzez funkcję przekazywania, która uzyskuje obiekt z pliku void* a następnie wywołuje funkcję członka.

W odpowiednim interfejsie C ++ możesz chcieć rzucić okiem na to, że twoja funkcja bierze szablonowy argument dla obiektów funkcji, aby używać dowolnych typów klas. Jeśli użycie szablonu na szablonie jest niepożądane, powinieneś użyć czegoś takiego std::function<void(int, int)>: możesz utworzyć dla nich odpowiedni obiekt funkcji do wywoływania, np. używając std::bind().

Podejście typu bezpiecznego przy użyciu argumentu szablonu dla typu klasy lub odpowiedniego std::function<...> są lepsze niż użycie a void* interfejs, ponieważ eliminują możliwość wystąpienia błędów spowodowanych rzutowaniem na niewłaściwy typ.

Aby wyjaśnić, jak używać wskaźnika funkcji do wywoływania funkcji składowej, 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);
}

92
2017-09-30 16:36



Ok, lubię tę odpowiedź! Czy możesz określić, co masz na myśli mówiąc "zadzwoń do swojego członka za pomocą funkcji przekazywania, która otrzyma obiekt z pustki *, a następnie wywoła funkcję członka" lub udostępni mu przydatny link? Dzięki - Jorge Leitão
Myślę, że rozumiem. (Edytowałem twój wpis) Dziękuję za wyjaśnienie i przykład, bardzo pomocny. Żeby potwierdzić: dla każdej funkcji członka, którą chcę wskazać, muszę wykonać spedytora. Dobrze? - Jorge Leitão
Cóż, tak, trochę. W zależności od tego, jak efektywnie korzystasz z szablonów, możesz uciec się do tworzenia szablonów przekazywania, które mogą współpracować z różnymi klasami i funkcjami składowymi. Jak to zrobić byłoby osobną funkcją, pomyślałem ;-) - Dietmar Kühl
Nie podoba mi się ta odpowiedź, ponieważ używa void*, co oznacza, że ​​możesz uzyskać bardzo nieprzyjemne błędy, ponieważ nie jest już sprawdzane. - Superlokkus
@Superlokkus: czy możesz oświecić nas alternatywą? - Dietmar Kühl


@Pete Becker's Odpowiedź jest w porządku, ale możesz to zrobić bez przekazywania class instancja jako jawny parametr dla function1 w 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;
}

54
2017-09-30 16:42



Jest void function1(std::function<void(int, int)>) poprawny? - Deqing
Musisz nadać argumentowi funkcja nazwę zmiennej, a następnie nazwa zmiennej jest tym, co faktycznie przekazujesz. Więc: void function1(std::function<void(int, int)> functionToCall) i wtedy functionToCall(1,1);. Próbowałem edytować odpowiedź, ale ktoś ją odrzucił, ponieważ nie ma sensu z jakiegoś powodu. Zobaczymy, czy w pewnym momencie zostanie wznowione. - Dorky Engineer
@DorkyEngineer To dość dziwne, myślę, że masz rację, ale nie wiem, jak ten błąd mógł pozostać niezauważony przez tak długi czas. W każdym razie, zredagowałem teraz odpowiedź. - Matt Phillips
znalazłem to stanowisko mówiąc, że istnieje poważna kara za wydajność ze std :: function. - kevin


Wskaźnik do funkcji elementu różni się od wskaźnika do funkcji. Aby użyć funkcji składowej przez wskaźnik, potrzebujesz do niej wskaźnika (oczywiście) i obiektu, do którego chcesz go zastosować. Tak więc odpowiednia wersja function1 byłoby

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

i nazwać to:

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

37
2017-09-30 16:35



Dziękuję Ci bardzo. Właśnie dowiedziałem się, że liczba nawiasów się liczy: function1 (& (aClass :: test), a) działa z MSVC2013, ale nie z gcc. gcc potrzebuje & przed nazwą klasy (co jest mylące, ponieważ operator & przyjmuje adres funkcji, a nie klasy) - kritzel_sw
myślałem function w void (aClass::*function)(int, int) był typem, ponieważ jest typem typedef void (aClass::*function)(int, int). - Olumide
@Oumide - typedef int X; definiuje typ; int X; tworzy obiekt. - Pete Becker


Od 2011 roku, jeśli możesz zmienić function1, zrób 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ż też, że poprawiłem definicję uszkodzonego obiektu (aClass a(); deklaruje funkcję).


0
2017-08-10 16:03





Możesz przestać walić teraz w głowy. Oto opakowanie dla funkcji elementu do obsługi istniejący funkcje biorące proste do działa jako argumenty. thread_local dyrektywa jest tutaj kluczowa.

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

Prosimy o komentowanie wszelkich problemów związanych z tym podejściem.

Inne odpowiedzi nie zadzwonić istniejący Równina C Funkcje: http://cpp.sh/8exun


0
2017-08-07 10:24



Zamiast więc używać std :: bind lub lambda do owinięcia instancji polegającej na zmiennej globalnej. Nie widzę żadnej korzyści z tego podejścia w porównaniu z innymi odpowiedziami. - super
@Super, Inne odpowiedzi nie mogą wywoływać istniejących funkcji, przyjmując proste C działa jako argumenty. - neckTwi
Pytanie brzmi, jak wywołać funkcję członka. Przejście w bezpłatnej funkcji już działa dla OP w pytaniu. Ty też nic nie przekazujesz. Są tu tylko funkcje z twardym kodowaniem i globalny wskaźnik Foo_. Jak ta skala, jeśli chcesz wywołać inną funkcję członka? Będziesz musiał przepisać podstawowe funkcje lub użyć różnych dla każdego celu. - super