Проблема с определением функции, которая может выполнять методы объекта и автономные функции - PullRequest
0 голосов
/ 29 марта 2019

Я застрял при определении функции, которая может выполнять автономные функции и методы объектов, используя шаблоны.

template <typename Func, typename ...Args>
void execute(Func&& f, Args&&... args)
{
    cout << "execute1" << endl;
    std::forward<decltype(f)>(f)(std::forward<Args>(args)...);
}

template <typename Func, typename Obj, typename ...Args>
void execute(Func&& fun, Obj&& obj, Args&&... args)
{
    cout << "execute2" << endl;
    std::forward<decltype(obj)>(obj).fun(args...);
}

void fun()
{
    cout << "in fun()" << endl;
}

class A
{
public:
    void method()
    {
        cout << "in method()" << endl;
    }
};

int main()
{
    A a;
    execute(fun);
    execute(&A::method, std::move(a));
    return 0;
}

Теперь, если я передам функцию fun () в качестве параметра, она будет корректно выполняться при первой перегрузке.

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

main.cpp: In instantiation of ‘void execute(Func&&, Obj&&, Args&& ...) [with Func = void (A::*)(); Obj = A; Args = {}]’:
main.cpp:20:38: error: ‘class A’ has no member named ‘fun’
     std::forward<decltype(obj)>(obj).fun(args...);

Я новичок в шаблонах, поэтому прошу прощения:)

1 Ответ

0 голосов
/ 29 марта 2019

Несколько выпусков:

Обе перегрузки жизнеспособны для execute(&A::method, std::move(a));, и первая из них лучше подходит. Так что твоя ошибка.

Вы можете использовать SFINAE для отмены недопустимых перегрузок:

template <typename Func, typename ...Args>
auto execute(Func&& f, Args&&... args)
-> decltype(std::forward<Func>(f)(std::forward<Args>(args)...))
{
    cout << "execute1" << endl;
    std::forward<Func>(f)(std::forward<Args>(args)...);
}

template <typename Func, typename Obj, typename ...Args>
auto execute(Func&& fun, Obj&& obj, Args&&... args)
-> decltype((std::forward<Obj>(obj).*fun)(std::forward<Args>(args)...);)
{
    cout << "execute2" << endl;
    (std::forward<Obj>(obj).*fun)(std::forward<Args>(args)...);
}

Как вы могли заметить, я также исправил синтаксис вызова метода с указателем метода (.* или ->*). Вторая перегрузка обрабатывает только ссылочный объект, а не (умные) указатели. Для этого потребуется добавить другую перегрузку.

В std у нас есть std::invoke (C ++ 17) с этим и даже больше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...