Как сохранить шаблон с переменным числом аргументов аргументы?
можно ли как-то сохранить пакет параметров для последующего использования?
template <typename... T>
class Action {
private:
std::function<void(T...)> f;
T... args; // <--- something like this
public:
Action(std::function<void(T...)> f, T... args) : f(f), args(args) {}
void act(){
f(args); // <--- such that this will be possible
}
}
позже:
void main(){
Action<int,int> add([](int x, int y){std::cout << (x+y);}, 3, 4);
//...
add.act();
}
3 ответов:
чтобы выполнить то, что вы хотите сделать здесь, вам придется хранить аргументы шаблона в кортеже:
std::tuple<Ts...> args;кроме того, вам придется немного изменить свой конструктор. В частности, инициализация
argsСstd::make_tupleа также позволяет универсальные ссылки в вашем списке параметров:template <typename F, typename... Args> Action(F&& func, Args&&... args) : f(std::forward<F>(func)), args(std::forward<Args>(args)...) {}кроме того, вам придется настроить генератор последовательности типа такого:
namespace helper { template <int... Is> struct index {}; template <int N, int... Is> struct gen_seq : gen_seq<N - 1, N - 1, Is...> {}; template <int... Is> struct gen_seq<0, Is...> : index<Is...> {}; }и вы можете реализовать свой метод с точки зрения одного взятия такой генератор:
template <typename... Args, int... Is> void func(std::tuple<Args...>& tup, helper::index<Is...>) { f(std::get<Is>(tup)...); } template <typename... Args> void func(std::tuple<Args...>& tup) { func(tup, helper::gen_seq<sizeof...(Args)>{}); } void act() { func(args); }и это все! Так что теперь ваш класс должен выглядеть так:
template <typename... Ts> class Action { private: std::function<void (Ts...)> f; std::tuple<Ts...> args; public: template <typename F, typename... Args> Action(F&& func, Args&&... args) : f(std::forward<F>(func)), args(std::forward<Args>(args)...) {} template <typename... Args, int... Is> void func(std::tuple<Args...>& tup, helper::index<Is...>) { f(std::get<Is>(tup)...); } template <typename... Args> void func(std::tuple<Args...>& tup) { func(tup, helper::gen_seq<sizeof...(Args)>{}); } void act() { func(args); } };вот ваша полная программа на Колиру.
Update: вот вспомогательный метод, с помощью которого спецификация аргументов шаблона не требуется:
template <typename F, typename... Args> Action<Args...> make_action(F&& f, Args&&... args) { return Action<Args...>(std::forward<F>(f), std::forward<Args>(args)...); } int main() { auto add = make_action([] (int a, int b) { std::cout << a + b; }, 2, 3); add.act(); }
можно использовать
std::bind(f,args...)для этого. Он будет генерировать подвижный и, возможно, копируемый объект, который хранит копию объекта функции и каждого из аргументов для последующего использования:#include <iostream> #include <utility> #include <functional> template <typename... T> class Action { public: using bind_type = decltype(std::bind(std::declval<std::function<void(T...)>>(),std::declval<T>()...)); template <typename... ConstrT> Action(std::function<void(T...)> f, ConstrT&&... args) : bind_(f,std::forward<ConstrT>(args)...) { } void act() { bind_(); } private: bind_type bind_; }; int main() { Action<int,int> add([](int x, int y) { std::cout << (x+y) << std::endl; }, 3, 4); add.act(); return 0; }обратите внимание, что
std::bindэто функция, и вам нужно сохранить, как член данных, результат ее вызова. Тип данных этого результата нелегко предсказать (стандарт даже не указывает его точно), поэтому я использую комбинациюdecltypeиstd::declvalчтобы вычислить этот тип данных во время компиляции. См. определениеAction::bind_typeвыше.Также обратите внимание, как я использовал универсальные ссылки в конструктор шаблона. Это гарантирует, что вы можете передавать аргументы, которые не соответствуют параметрам шаблона класса
T...точно (например, вы можете использовать ссылки rvalue на некоторые изTи вы получите их переслали вbindзвонок.)заключительное Примечание: Если вы хотите сохранить аргументы как ссылки (так что функция, которую вы передаете, может изменять, а не просто использовать их), вам нужно использовать
std::refчтобы обернуть их в эталонных объектов. Просто проходяT &создать копию значения, а не ссылка.
Я думаю, что у вас есть проблема XY. Зачем идти на все проблемы, чтобы сохранить пакет параметров, когда вы можете просто использовать лямбда на callsite? то есть,
#include <functional> #include <iostream> typedef std::function<void()> Action; void callback(int n, const char* s) { std::cout << s << ": " << n << '\n'; } int main() { Action a{[]{callback(13, "foo");}}; a(); }
Comments