Как напрямую связать функцию-член с std::function в Visual Studio 11?

Я могу легко связать функции-члены с std::function, обернув их лямбда-выражением с предложением захвата.

class Class
{
    Class()
    {
        Register([=](int n){ Function(n); });
    }

    void Register(std::function<void(int)> Callback)
    {

    }

    void Function(int Number)
    {

    }
};

Но я хочу связать их напрямую, что-то вроде следующего.

// ...
Register(&Class::Function);
// ...

Я думаю, что в соответствии со стандартом С++ 11 это должно поддерживаться. Однако в Visual Studio 11 я получаю эти ошибки компилятора.

ошибка C2440: «новая строка»: невозможно преобразовать из «int» в «Class *»

ошибка C2647: '.*': невозможно разыменовать 'void (__thiscall Class::*)(int)' на 'int'


person danijar    schedule 16.06.2013    source источник


Ответы (5)


Я думаю, что в соответствии со стандартом С++ 11 это должно поддерживаться

Не совсем, потому что нестатическая функция-член имеет неявный первый параметр типа (cv-qualified) YourType*, поэтому в данном случае он не соответствует void(int). Отсюда необходимость std::bind:

Register(std::bind(&Class::Function, PointerToSomeInstanceOfClass, _1));

Например

Class c;
using namespace std::placeholders; // for _1, _2 etc.
c.Register(std::bind(&Class::Function, &c, _1));

Изменить Вы упомянули, что это должно вызываться с тем же экземпляром Class. В этом случае вы можете использовать простую функцию, не являющуюся членом:

void foo(int n)
{
  theClassInstance.Function(n);
}

потом

Class c;
c.Register(foo);
person juanchopanza    schedule 16.06.2013
comment
Работает, но можно ли упростить синтаксис при условии, что все ссылки на классы одинаковы? - person danijar; 16.06.2013
comment
@danijar, что вы имеете в виду, говоря, что все ссылки на классы одинаковы? Вам нужно передать указатель на экземпляр Class. Какой из них вы пройдете, зависит от вас. - person juanchopanza; 16.06.2013
comment
Верно. И этот экземпляр всегда будет одним и тем же. Теперь я хотел бы сократить bind(&Class::Function, this, _1) с учетом этой информации. Итак, по содержанию bind устарел. Есть ли технический способ избавиться от него или выполнить привязку внутри функции после передачи? - person danijar; 16.06.2013
comment
@danijar это будет какой-то глобальный экземпляр или синглтон? Если это так, вы также можете создать глобальную std::function, где вы привязываетесь к этому экземпляру. Затем вы просто повторно используете это. На самом деле нет необходимости хранить глобальный std::function. Простая функция, не являющаяся членом, может помочь. - person juanchopanza; 16.06.2013
comment
Нет, к сожалению, это не глобальный экземпляр. - person danijar; 16.06.2013
comment
@danijar так что это? Может быть, вы можете опубликовать код, чтобы прояснить это? - person juanchopanza; 16.06.2013
comment
Это просто самостоятельный класс с различными вариантами использования. Но если нет возможности, я приму это. - person danijar; 16.06.2013
comment
@juanchopanza, что означает void (int n) foo? - person Sasha; 28.10.2019
comment
@Саша Наверное void foo(int n). Исправлена. - person juanchopanza; 29.10.2019

По словам Стефана Т. Лававея: «Избегайте использования bind (), ..., используйте лямбда-выражения». https://www.youtube.com/watch?v=zt7ThwVfap0&t=32m20s

В таком случае:

Class()
{
    Register([this](int n){ Function(n); });
}
person Andy    schedule 13.04.2016
comment
спасибо за указание на это и ссылку на соответствующую часть видео! - person Nicolas Holthaus; 19.09.2016

Вы можете использовать std::bind:

using namespace std::placeholders;  // For _1 in the bind call

// ...

Register(std::bind(&Class::Function, this, _1));
person Some programmer dude    schedule 16.06.2013
comment
Скажем, я хочу установить этот указатель функции в качестве аргумента по умолчанию для функции. Это возможно? Тогда я не могу использовать такие вызовы, как bind, не так ли? У меня нет указателя this, чтобы найти его в списке параметров. - person danijar; 16.06.2013
comment
@danijar Это невозможно, но это можно обойти, используя перегруженный вариант Register без аргументов, который привязывается к вашей функции по умолчанию. - person Some programmer dude; 16.06.2013
comment
Мне не хватало std::placeholders в моем коде, и этот ответ указал мне на это! - person Martin Meeser; 08.02.2016

В С++ 17 вы можете использовать:

Register([=](auto && ...args){ return Function(args...); });

что приятно, особенно если список аргументов длиннее. Конечно, список аргументов функции-члена должен быть совместим со списком аргументов std::function.

person peterchen    schedule 19.07.2019

С помощью std::function и std::bind вы можете одинаково обрабатывать разные функции-члены класса.

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

class Foo
{
public:
    void foo(const string &msg)
    {
        cout << msg << '\n';
    }
};

class Bar
{
public:
    void bar(const string &msg, const string &suffix)
    {
        cout << msg << suffix << '\n';
    }
};

int main(int argc, char **argv)
{
    Foo foo;
    Bar bar;

    vector<function<void (const string &msg)>> collection;
    collection.push_back(bind(&Foo::foo, &foo, _1));
    collection.push_back(bind(&Bar::bar, &bar, _1, "bar"));

    for (auto f : collection) {
        f("foo");
    }

    return 0;
}
person Richard    schedule 13.08.2013