Функция, принимающая указатель на метод независимо от константы
Я хочу реализовать универсальную функцию, которая будет использовать ссылку на объект и указатель на свою функцию-член и вызывать ее. Однако я не могу этого сделать, если в моем классе есть как константные, так и неконстантные методы, так как мне нужно предоставить две перегрузки:
template<typename Ret, typename Class, typename ...Us>
Ret callMethod(Class &object, Ret (Class::*method)(Us...))
{
return (object.*method)(Us{}...);
}
template<typename Ret, typename Class, typename ...Us>
Ret callMethod(Class &object, Ret (Class::*method)(Us...) const)
{
return (object.*method)(Us{}...);
}
Есть ли способ написать только одну функцию-шаблон, которая будет принимать как константные, так и неконстантные указатели на методы, чтобы мне не приходилось писать код дважды? Я использую C ++ 14.
Для более широкой картины, в конечном итоге я хочу передать третий параметр, буфер данных, из которого будут извлечены аргументы метода - следовательно, функция шаблона будет обрабатывать его как можно более обобщенно.
Ответы
Вот альтернатива C ++ 14 без использования std::function
.
то, что я хочу в конечном итоге достичь, - это передать третий параметр, буфер данных, из которого будут извлечены аргументы метода - следовательно, функция шаблона для обработки его как можно более обобщенно
То, что вы используете на сайте вызова, будет идеально перенаправлено сюда:
template<typename Class, typename Func, typename... Args>
decltype(auto) callMethod_impl(Class& object, Func method, Args&&... args) {
return (object.*method)(std::forward<Args>(args)...);
}
template<typename Ret, typename Class, typename... Us, typename... Args>
Ret callMethod(Class& object, Ret(Class::*method)(Us...), Args&&... args) {
return callMethod_impl(object, method, std::forward<Args>(args)...);
}
template<typename Ret, typename Class, typename... Us, typename... Args>
Ret callMethod(const Class& object, Ret(Class::*method)(Us...) const, Args&&... args) {
return callMethod_impl(object, method, std::forward<Args>(args)...);
}
Демо
Если вам нужно Ret
в callMethod_impl
, просто добавьте его в качестве параметра шаблона и назовите его как callMethod_impl<Ret>(...)
от callMethod
перегрузок ( Demo ).
Короткий ответ: не реализуйте это самостоятельно, это уже было сделано за вас в виде std::invoke:
#include <functional>
struct A {
void foo(int x);
void bar(int x) const;
};
void example() {
A a;
std::invoke(&A::foo, a, 3);
std::invoke(&A::bar, a, 3);
}
Видя, что вы ретроспективно добавили тег C ++ 14, в документации std::invokeесть образец реализации, который вы можете использовать в своем проекте.
Без него std::invoke
вы можете найти только некоторые более или менее общие обходные пути, основанные на стирании типа. Использование std::function
позволяет вам создать прокси-функцию, которая будет относиться к вашим функциям на равных условиях:
#include <iostream>
#include <functional>
template<typename Ret, typename ...Us>
Ret callMethod_impl(std::function<Ret(Us...)> f) {
// common implementation here
return f(Us{}...);
}
template<typename Ret, typename Class, typename ...Us>
Ret callMethod(Class &object, Ret (Class::*method)(Us...)) {
return callMethod_impl(std::function<Ret(Us...)>(std::bind(method, object)));
}
template<typename Ret, typename Class, typename ...Us>
Ret callMethod(const Class &object, Ret (Class::*method)(Us...) const) {
return callMethod_impl(std::function<Ret(Us...)>(std::bind(method, object)));
}
struct Foo {
int bar() const { return 1; }
float bar() { return 2.1f; };
};
int main() {
Foo f;
std::cout << callMethod(f, &Foo::bar) << std::endl;
}
Обратите внимание, что volatile
функции (+ комбинации) здесь не рассматриваются, но, возможно, вам не нужна полная общность, а скорее прагматическое решение.