Как сохранить шаблон с переменным числом аргументов аргументы?



можно ли как-то сохранить пакет параметров для последующего использования?



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();
}
577   3  

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 & создать копию значения, а не ссылка.

рабочий код на Coliru

Я думаю, что у вас есть проблема 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

    Ничего не найдено.