Jak przechowywać zmienne argumenty szablonu?
Czy możliwe jest przechowywanie parametru pack w jakiś sposób do późniejszego wykorzystania?
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
}
}
Potem:
void main(){
Action<int,int> add([](int x, int y){std::cout << (x+y);}, 3, 4);
//...
add.act();
}
3 answers
Aby osiągnąć to, co chcesz tutaj zrobić, musisz zapisać argumenty szablonu w krotce:
std::tuple<Ts...> args;
Ponadto, będziesz musiał nieco zmienić swój konstruktor. W szczególności inicjalizowanie {[6] } za pomocą std::make_tuple
, a także zezwalanie na uniwersalne odwołania na liście parametrów:
template <typename F, typename... Args>
Action(F&& func, Args&&... args)
: f(std::forward<F>(func)),
args(std::forward<Args>(args)...)
{}
Co więcej, będziesz musiał skonfigurować generator sekwencji podobny do tego:
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...> {};
}
I możesz zaimplementować swoją metodę w kategoriach jednego z takich generatorów:
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);
}
I to jest to! Więc teraz twoja klasa powinna wyglądać tak:
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);
}
};
Oto twój pełny program na Coliru.
Update: oto metoda pomocnicza, dzięki której Specyfikacja argumentów szablonu nie jest konieczna:
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();
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-16 22:31:24
Możesz użyć std::bind(f,args...)
za to. Generuje ruchomy i ewentualnie kopiowalny obiekt, który przechowuje kopię obiektu funkcji i każdego z argumentów do późniejszego użycia:
#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;
}
Zauważ, że {[2] } jest funkcją i musisz zapisać, jako element danych, wynik jej wywołania. Typ danych tego wyniku nie jest łatwy do przewidzenia (Standard nawet nie precyzuje go precyzyjnie), więc używam kombinacji decltype
i std::declval
aby obliczyć ten typ danych w czas kompilacji. Zobacz definicję Action::bind_type
powyżej.
Zwróć również uwagę, jak używałem uniwersalnych odniesień w konstruktorze szablonów. Zapewnia to, że możesz przekazać argumenty, które nie pasują dokładnie do parametrów szablonu klasy T...
(np. możesz użyć referencji rvalue do niektórych z T
i otrzymasz je przekazane jako-jest do wywołania bind
.)
Ostatnia uwaga: jeśli chcesz przechowywać argumenty jako referencje (tak, aby przekazywana funkcja mogła je modyfikować, a nie tylko używać), musisz użyć std::ref
, aby zawinąć je w obiekty referencyjne. Podanie T &
spowoduje utworzenie kopii wartości, a nie referencji.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-01 09:47:30
Myślę, że masz problem z XY. Po co trudzić się przechowywaniem pakietu parametrów, skoro można po prostu użyć lambda na miejscu połączenia? tzn.,
#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();
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-01 04:28:30