Как передать объект Callable в c ++ 17 для использования с std :: invoke - PullRequest
4 голосов
/ 14 января 2020

Почему следующее не компилируется при передаче i в конструктор. Другие подобные конструкции компилируются.

#include <iostream>
#include <functional>

int RetXPrintU(int x, uint u)
{
    std::cout << "RetXprintU(): " << u << std::endl;
    return x;
}

template <typename Fn, typename... Args>
void Call(Fn&& fun, Args&&... args)
{
    std::invoke(std::forward<Fn>(fun), std::forward<Args>(args)...);
}

template <typename Fn, typename... Args>
class CallableObj
{
public:
    explicit CallableObj(Fn&& fun, Args&&... args)
    {
        std::invoke(std::forward<Fn>(fun), std::forward<Args>(args)...);
    }
};

int main() {
    int i = 4;
    std::invoke(RetXPrintU, i, 8u);
    Call(RetXPrintU, i, 8u);
    CallableObj co(RetXPrintU, i, 8u); // WHY I DO NOT COMPILE?
    //CallableObj co(RetXPrintU, 0, 8u); // WHY I COMPILE?

    return 0;
}

Ответы [ 2 ]

4 голосов
/ 14 января 2020

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

template <typename Fn, typename... Args>
class CallableObj
{
public:
    explicit CallableObj(Fn&& fun, Args&&... args)
    {
        std::invoke(std::forward<Fn>(fun), std::forward<Args>(args)...);
    }
};

Args&&... не является ссылкой переадресации c, потому что она выводится для самого класса, а когда вы вызываете CallableObj co(RetXPrintU, i, 8u);, создается экземпляр класса, и конструктор выделяется как

explicit CallableObj(int(int, unsigned int)&& fun, int&& arg1, unsigned int&& arg2)

То, что мы хотим, это

class CallableObj
{
public:
    template <typename Fn, typename... Args>
    explicit CallableObj(Fn&& fun, Args&&... args)
    {
        std::invoke(std::forward<Fn>(fun), std::forward<Args>(args)...);
    }
};

, так что теперь Args будет выводиться при вызове конструктора, а Args&& теперь является ссылкой для пересылки.

Причина, по которой CallableObj co(RetXPrintU, 0, 8u); работал в вашем примере, заключается в том, что 0 является prvalue, и prvalue может связываться со ссылкой на rvalue.

3 голосов
/ 14 января 2020

Как говорится в сообщении об ошибке, компилятор не может определить параметры шаблона для CallableObj. Для этого вам нужно добавить руководство по выводу:

template <typename Fn, typename... Args>
CallableObj(Fn&& fun, Args&&... args) -> CallableObj<Fn, Args>...>;

Весь код выглядит так:

template <typename Fn, typename... Args>
class CallableObj
{
public:
    explicit CallableObj(Fn fun, Args... args)
    {
        std::invoke(std::forward<Fn>(fun), std::forward<Args>(args)...);
    }
};

template <typename Fn, typename... Args>
CallableObj(Fn&& fun, Args&&... args) -> CallableObj<Fn, Args...>;

Как указывал @ Jarod42, нет необходимости делать конструктор самим шаблоном (так как было в первой версии ответа).

А вот живой пример (исправленная версия @ Jarod42).

Я предполагал, что ваш код просто минимальный пример, и вам нужно, чтобы класс был шаблоном. Если это не так, вам лучше go с другим решением.

...