Создание std :: function с функцией-членом не компилируется - PullRequest
1 голос
/ 07 ноября 2019

Я сталкиваюсь с проблемами компиляции с классом C ++.

Мой код здесь:

#include <chrono>
#include <cstdint>
#include <thread>
#include <functional>

class Monitors {

public:

    Monitors(std::uint32_t timeout1, std::uint32_t timeout2) : timeout_1(timeout1), timeout_2(timeout2) {
        timer_start(task_1, timeout1); //does not compile
        timer_start(&Monitors::task_2, timeout2); //does not compile either

    }

private:

    void timer_start( std::function<void(void)>& func, std::uint32_t interval) {
        std::thread([func, interval]() {
            while (true) {
                func();
                std::this_thread::sleep_for(std::chrono::milliseconds(interval));                }
        }).detach();
    }

    void task_1() {
    }

    void task_2() {
    }


private:

    std::uint32_t timeout_1;
    std::uint32_t timeout_2;
};

Ошибка:

non const lvalue toТип std :: function не может привязаться к временному типу void

В чем проблема? Как я могу это исправить?

1 Ответ

3 голосов
/ 07 ноября 2019

Компилятор должен преобразовать функцию-член в объект std::function<void()>, чтобы он соответствовал списку аргументов функции timer_start.

Этот объект является временным объектом, время жизни которого закончится после вызова timer_start сделано. А временные объекты не могут быть привязаны к непостоянным ссылкам (это то, о чем говорится в сообщении об ошибке).

Простое решение - передать функцию по значению .

void timer_start( std::function<void(void)> func, uint32_t interval)

Вторая проблема заключается в том, что для функции-члена вам всегда нужно использовать оператор address-of, чтобы получить указатель на него. И вам всегда нужно указывать также класс.

Что означает, что вам нужно сделать

timer_start(&Monitors::task_1, timeout1);
timer_start(&Monitors::task_2, timeout2);

Наконец, третья проблема: нестатические функции-члены являются особенными, в этомим нужен объект для вызова. Этот объект становится указателем this внутри функции.

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

Таким образом, окончательное объявление должно быть

void timer_start( std::function<void(Monitors*)> func, uint32_t interval)

Последнее также означает, что вам нужно передать указатель на объект функции, в вашем случае вы можете передать this:

// Capture this
//           vvvv
std::thread([this, func, interval]() {
    while (true) {
        auto x = std::chrono::steady_clock::now() + std::chrono::milliseconds(interval);
        func(this);
        //   ^^^^
        // Pass this as argument
        std::this_thread::sleep_until(x);
    }
}).detach();
...