¿Cómo almacenar argumentos de plantilla variadic?

¿Es posible almacenar un paquete de parámetros de alguna manera para un uso posterior?

template  class Action { private: std::function f; T... args; // <--- something like this public: Action(std::function f, T... args) : f(f), args(args) {} void act(){ f(args); // <--- such that this will be possible } } 

Luego más adelante:

 void main(){ Action add([](int x, int y){std::cout << (x+y);}, 3, 4); //... add.act(); } 

Para lograr lo que quiere hacer aquí, tendrá que almacenar sus argumentos de plantilla en una tupla:

 std::tuple args; 

Además, tendrás que cambiar un poco tu constructor. En particular, inicializar args con std::make_tuple y también permitir referencias universales en su lista de parámetros:

 template  Action(F&& func, Args&&... args) : f(std::forward(func)), args(std::forward(args)...) {} 

Además, tendrías que configurar un generador de secuencia muy parecido a esto:

 namespace helper { template  struct index {}; template  struct gen_seq : gen_seq {}; template  struct gen_seq<0, Is...> : index {}; } 

Y puede implementar su método en términos de uno que tome dicho generador:

 template  void func(std::tuple& tup, helper::index) { f(std::get(tup)...); } template  void func(std::tuple& tup) { func(tup, helper::gen_seq{}); } void act() { func(args); } 

¡Y eso! Entonces ahora tu clase debería verse así:

 template  class Action { private: std::function f; std::tuple args; public: template  Action(F&& func, Args&&... args) : f(std::forward(func)), args(std::forward(args)...) {} template  void func(std::tuple& tup, helper::index) { f(std::get(tup)...); } template  void func(std::tuple& tup) { func(tup, helper::gen_seq{}); } void act() { func(args); } }; 

Aquí está su progtwig completo en Coliru.


Actualización: Aquí hay un método auxiliar por el cual la especificación de los argumentos de la plantilla no es necesaria:

 template  Action make_action(F&& f, Args&&... args) { return Action(std::forward(f), std::forward(args)...); } int main() { auto add = make_action([] (int a, int b) { std::cout << a + b; }, 2, 3); add.act(); } 

Y de nuevo, aquí hay otra demo.

Puede usar std::bind(f,args...) para esto. Generará un objeto movible y posiblemente copiable que almacena una copia del objeto de función y de cada uno de los argumentos para su uso posterior:

 #include  #include  #include  template  class Action { public: using bind_type = decltype(std::bind(std::declval>(),std::declval()...)); template  Action(std::function f, ConstrT&&... args) : bind_(f,std::forward(args)...) { } void act() { bind_(); } private: bind_type bind_; }; int main() { Action add([](int x, int y) { std::cout << (x+y) << std::endl; }, 3, 4); add.act(); return 0; } 

Observe que std::bind es una función y necesita almacenar, como miembro de datos, el resultado de llamarlo. El tipo de datos de ese resultado no es fácil de predecir (el estándar ni siquiera lo especifica con precisión), así que utilizo una combinación de decltype y std::declval para calcular ese tipo de datos en tiempo de comstackción. Consulte la definición de Action::bind_type anterior.

También observe cómo utilicé referencias universales en el constructor con plantilla. Esto asegura que puede pasar argumentos que no coinciden con los parámetros de la plantilla de clase T... exactamente (por ejemplo, puede usar referencias rvalue a algunas de las T y las enviará tal como están a la llamada de bind ).

Nota final: si desea almacenar argumentos como referencias (para que la función que aprueba pueda modificar, en lugar de simplemente usarlos), necesita usar std::ref para envolverlos en objetos de referencia. Simplemente al pasar un T & creará una copia del valor, no una referencia.

Código operacional en Coliru

Creo que tienes un problema XY. ¿Por qué tomarse tantas molestias para almacenar el paquete de parámetros cuando podría usar una lambda en el callsite? es decir,

 #include  #include  typedef std::function Action; void callback(int n, const char* s) { std::cout << s << ": " << n << '\n'; } int main() { Action a{[]{callback(13, "foo");}}; a(); }