Как использовать std::bind с методами класса?

221
02 июля 2022, 21:50

Пытаюсь использовать std::bind с методом класса:

#include <iostream>
#include <functional>
class Test{
public:
    void print(int a){
        std::cout << a << std::endl;
    }
};

int main() {
    Test test;
    std::bind(test, test.print, 5);
    return 0;
}

Компилятор выдает следующую ошибку:

Reference to non-static member function must be called.

Как исправить?

Answer 1

Прежде, чем писать на все знакомые форумы первым делом лучше почитать документацию - там много полезного написано. Например сигнатура std::bind:

template< class F, class... Args >
/*unspecified*/ bind( F&& f, Args&&... args );

Как видим, первым аргументом должен идти функтор, а у вас, почему-то объект test.

Исправим:

std::bind(test.print, 5);

Однако получаем ошибку при попытке скомпилировать, на этот раз что-то вроде invalid non-static member function - это потому что мы не можем вот так обращаться к методам объектам, вместо этого указатель на метод записывается так:

std::bind(&Test::print, 5);

Однако, как мы видим, мы имеем указатель на метод, но нигде не указываем сам объект - нам компилятор на этот раз сообщит о невалидном количестве параметров в функции. Все потому, что в методах (не статических) всегда есть минимем один аргумент - это this. И хоть он не указывается при определении метода, это тем не менее самый настоящий аргумент.

Как результат в итге получаем:

std::bind(&Test::bind, &test, 5);

UDP Тут в комментах также подсказали, что можно к методу биндить не только указатель, но и ссылку - это абсолютно верно, но есть одно большое НО, о чем автор коммента забыл упомянуть. Давайте разберемся что за но.

  std::bind(&Test::print, test, 5);

С первого взгляда все ок, все работает как ожидается, но давайте немного усложним класс Test. Предположим это объект который содержит сокет и во время вызова print пишет что-то в сокет. Сокет такая вещь, которую лучше не копировать, поэтому пометим наш класс как nocopyable

class Test {
...
Test() = default;
Test(const Test &) = delete;
}

И что же мы увидим при попытке компиляции? Ошибку. Компилятор нам сообщит, что не может скопировать объект test чтобы использовать его в std::bind. Как видим, подобныя ситуация может быть весьма и весьма чревата в реальной ситуации. К счастью она весьма просто исправляется: нужно указать, что мы передаем test именно по ссылке

std::bind(&Test::print, std::ref(test), 5);

Данные момент нужно знать обязательно, так как не зная об этом вы можете провести много времени в дебаге пытаяся разобраться в том, почему изменяя объект вы никак не влияете на результат

READ ALSO
Преобразование типа вектора из uint8 в uint16

Преобразование типа вектора из uint8 в uint16

Есть вектор std::vector<uint8_t> From; Он содержит 10^9 элементов

208
Помогите решить проблему с кодом на Arduino

Помогите решить проблему с кодом на Arduino

Прошу, помогите найти код в ошибке и решить проблему, а то моя тупая головушка не может понять конкретного местаПрошу, не судите строго, я новичок...

217
Как присвоить unsigned char const * к void*?

Как присвоить unsigned char const * к void*?

У меня не много опыта с С++, но вот, что мне нужно сделатьУ меня есть метод который должен скастить unsigned char const * к void*

224