Зачем нужен std::invoke?

183
12 августа 2018, 20:40

Увидел сейчас, что в 17ом стандарте появилась новая шаблонная функция std::invoke. Очень сильно обрадовался, т.к. подумал, что это такой же invoke как в .NET, но поискав информацию о нём, так до конца и не понял, зачем он нужен. Я так понял, что он делает вызов функторов и лямбд, но чем это отличается от их прямого вызова? Может ли std::invoke делать этот вызов в определённом потоке, как это умеет делать его собрат из .NET?

Answer 1

std::invoke нужен чтобы унифицированным образом вызвать функторы (в т.ч. лямбды), указатели на функции и указатели на функции-члены классов. Последние имеют специфический синтаксис вызова не совпадающий с синтаксисом обычных функторов:

(obj->*mem_fn_ptr)( args... );

Если вы пишите функцию типа std::async, и вам нужно реализовать поддержку всех callable объектов, то проще передать их одним пакетом в invoke, чем писать отдельную шаблонную реализацию для каждого случая. Такой синтаксический сахар.

Аналога .NET invoke в стандарте нет, потому что нет циклов обработки сообщений, и соответственно, "послать задачу в поток" невозможно. Воспользуйтесь std::async, либо std::thread, если нужно просто асинхронное выполнение, либо функциями WinAPI, либо функциями вашей оконной библиотеки (Qt, wxWidgets...), либо организуйте цикл обработки сообщений "вручную", и запихивайте упакованную задачу в соответствующую очередь.

Answer 2

Смысл std::invoke в вызове обычных методов и нестатических методов класса в единообразной манере. Это прежде всего полезно при написании шаблонов. Вот например компактный шаблон функции, оборачивающий вызов другой функции:

template<typename F, typename V1, typename V2> auto
foo(F f, V1 v1, V2 v2) -> void
{
    ::std::invoke(f, v1, v2);
}

Пример использования:

inline auto
bar(int v1, int v2) -> void
{
    ::std::cout << v1 << " " << v2 << ::std::endl;
}
class Bar
{
    private: int m_v;
    public: explicit
    Bar(int v): m_v{v} {}
    public: auto
    bar(int v2) -> void
    {
        ::std::cout << m_v << " " << v2 << ::std::endl;
    }
};
int
main()
{
    int v1{12};
    int v2{23};
    foo(&bar, v1, v2); // вызов обычного метода
    Bar b{12};
    foo(&Bar::bar, b, v2); // вызов нестатического метода класса по ссылке
    foo(&Bar::bar, &b, v2); // вызов нестатического метода класса по указателю
}

Без использования ::std::invoke пришлось бы самостоятельно реализовывать варианты для вызова нестатических методов класса:

template<typename F, typename V1, typename V2> auto
foo(F f, V1 v1, V2 v2) -> ::std::enable_if_t
<
    not ::std::is_member_function_pointer_v<F>
>
{
    f(v1, v2);
}
template<typename F, typename V1, typename V2> auto
foo(F f, V1 v1, V2 v2) -> ::std::enable_if_t
<
    ::std::is_member_function_pointer_v<F> and not ::std::is_pointer_v<V1>
>
{
    (v1.*f)(v2);
}
template<typename F, typename V1, typename V2> auto
foo(F f, V1 v1, V2 v2) -> ::std::enable_if_t
<
    ::std::is_member_function_pointer_v<F> and ::std::is_pointer_v<V1>
>
{
    (v1->*f)(v2);
}
Answer 3

От прямого вызова это отличается тем, что не всё что умеет вызывать std::invoke, можно вызвать напрямую.

Упрощенно говоря, вызов std::invoke(f, arg0, args...) пытается сделать один из следующих вызовов:

(arg0.*f)(args...);
(arg0.get().*f)(args...);
((*arg0).*f)(args...);
arg0.*f
arg0.get().*f;
(*arg0).*f;
f(arg0, args...);

Использование std::invoke требуется в тех случаях, когда вы пишите обобщенный код, способный работать с любым переданным Callable-объектом.

Что же до вызова функции в фоновом потоке - то для этих целей придумана функция std::async.

READ ALSO
Как обращаться к QStandardItemModel в потоке (QThread)?

Как обращаться к QStandardItemModel в потоке (QThread)?

Мне надо обрабатывать отображение миниатюр фотографий в отдельном потоке, иначе код очень долго обрабатываетсяУ меня есть в основном классе...

225
Выполнение/завершение по датам

Выполнение/завершение по датам

Использую cron и quartzНужно реализовать следующие: Администратор назначает на конкретную дату некое задание, которое обновляет базу данных

193
Java + Python на сервере

Java + Python на сервере

У меня есть серверное приложение на JavaВозникла необходимость запускать пользовательские скрипты, для этого я решил использовать Python, так...

242
Java spring hibernate lazy загрузка вне сессии

Java spring hibernate lazy загрузка вне сессии

Я использую Spring Data JPA (Hibernate)

179