Как я могу передать функцию-член, где ожидается свободная функция?

Вопрос заключается в следующем: рассмотрим этот кусок кода:

#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 aClass::test function?

    return 0;
}

Как я могу использовать a aClass::test в качестве аргумента function1? Я застрял в этом.

Я хотел бы получить доступ к члену класса.

Ответ 1

Нет ничего плохого в использовании указателей функций. Однако указатели на нестатические функции-члены не похожи на обычные указатели на функции: функции-члены должны быть вызваны на объект, который передается как неявный аргумент функции. Подпись вашей функции-члена выше, таким образом,

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

а не тип, который вы пытаетесь использовать

void (*)(int, int)

Один подход может заключаться в создании функции-члена static, и в этом случае он не требует вызова какого-либо объекта, и вы можете использовать его с типом void (*)(int, int).

Если вам нужен доступ к любому нестационарному члену вашего класса, и вам нужно придерживаться указателей на функции, например, потому что эта функция является частью интерфейса C, ваш лучший вариант - всегда передавать void* на ваш функция, вызывающая указатели на функции и вызывающая ваш член через функцию пересылки, которая получает объект из void*, а затем вызывает функцию-член.

В правильном интерфейсе С++ вы можете захотеть взглянуть на то, что ваша функция принимает шаблонный аргумент для объектов функции для использования произвольных типов классов. Если использование шаблонного интерфейса нежелательно, вы должны использовать что-то вроде 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);
}

Ответ 2

@Pete Becker отвечает хорошо, но вы также можете сделать это, не передавая экземпляр class в качестве явного параметра для function1 в 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;
}

Ответ 3

Указатель на функцию-член отличается от указателя на функцию. Чтобы использовать функцию-член через указатель, вам нужен указатель на него (очевидно) и объект для его применения. Поэтому подходящей версией function1 будет

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

и называть его:

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

Ответ 4

С 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(); объявляет функцию).

Ответ 5

Я задал похожий вопрос (C++ openframeworks пропустил void от других классов), но ответ, который я нашел, был более ясным, поэтому вот объяснение будущих записей:

проще использовать 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()});

где вы создаете лямбду, которая вызывает объект, захватывая его по ссылке

Ответ 6

Вы можете прекратить стучать головой сейчас. Вот оболочка для функции-члена для поддержки существующих функций, принимающих в качестве аргументов простые функции языка Си. Директива 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

Ответ 7

Я сделал функцию-член статической, и все работает:

#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 aClass::test function?

    getchar();return 0;
}