Я пытаюсь написать функцию, которая получает вызываемый объект в качестве параметра и возвращает измененную версию этого вызываемого объекта.
Вот пример того, что я пытаюсь создать:
template<class R, class... Args>
std::function<R(Args...)> ModifyFunction(std::function<R(Args...)> f)
{
return [f](Args... args) {
// Take some extra actions here...
return f(std::forward<Args>(args)...);
};
}
Он отлично работает, если я передаю ему объект std::function
:
std::function<void()> f([] {
std::cout << "Test" << std::endl;
});
auto modified = ModifyFunction(f);
modified();
Но он не скомпилируется, если я передам другой вызываемый тип, например, лямбда:
auto modified = ModifyFunction([] {});
modified();
Можно ли написать функцию, которая будет работать с любым вызываемым типом? Предпочтительно с C++11.
Связанное / обман о том, почему это не удается: stackoverflow.com/questions/21586756/…
некоторые обходные пути: stackoverflow.com/questions/70468317/…
@Someprogrammerdude Если я использую один аргумент шаблона, как я могу получить Args
? Они нужны мне для создания внутренней лямбды
@Gils Lambdas может быть общим: [f](auto... args) { ... }
@NathanOliver: общая лямбда-это C++14, а не C++11...
Упс, пропустил тег C++11.
@NathanOliver Даже в C++11 вы можете написать эквивалентный универсальный вызываемый объект: template<typename R, typename...Args> struct callable { R operator()(Args... value); };
и теперь, если вы передадите callable{}
, какой std::function
следует вывести?
@RaymondChen Суть в том, чтобы не выводить std::function
, а просто принимать любой вызываемый тип.
@NathanOliver Извините, мой комментарий был неоднозначным. Я основывался на вашем ответе, а не оспаривал его.
Вы можете избавиться от std::function
и сделать
в C++14 мы просто используем auto
в качестве возвращаемого типа:
template <typename F>
auto ModifyFunction(F f)
{
return [f](auto... args) -> decltype(f(std::forward<decltype(args)>(args)...)){
// Take some extra actions here...
return f(std::forward<decltype(args)>(args)...);
};
}
В C++11 вы можете создать именованный класс вместо лямбда-выражения.
template <typename F>
class Wrapper
{
F f;
public:
explicit Wrapper(F f) : f(std::move(f)) {}
template <typename...Args>
auto operator()(Args&&... args) -> decltype(f(std::forward<Args>(args)...))
{
// Take some extra actions here...
return f(std::forward<Args>(args)...);
}
};
template <typename F>
Wrapper<F> ModifyFunction(F f)
{
return Wrapper<F>(f);
}
Красивый ответ
Я рекомендую вам взглянуть на то, как сама стандартная библиотека обрабатывает вызываемые объекты: используя один аргумент шаблона (т. е.
template<typename F> std::function<F> ModfyFunction(F fun) { ... }
или что-то подобное).