2013-05-30 3 views
8

Я пытаюсь узнать о вариативных шаблонах в C++ 11. У меня есть класс, который в основном является оберткой вокруг std::array. Я хочу иметь возможность передавать функциональные объекты (в идеале lambdas) в функцию-член, а затем передавать элементы std::array в качестве параметров объекта функции.Передача std :: array в качестве аргументов переменной вариационной функции

Я использовал static_assert, чтобы проверить, что количество параметров соответствует длине массива, но я не могу придумать способ передать элементы в качестве аргументов.

Вот код

#include <iostream> 
#include <array> 
#include <memory> 
#include <initializer_list> 

using namespace std; 

template<int N, typename T> 
struct Container { 
    template<typename... Ts> 
    Container(Ts&&... vs) : data{{std::forward<Ts>(vs)...}} { 
     static_assert(sizeof...(Ts)==N,"Not enough args supplied!"); 
    } 

    template< typename... Ts> 
    void doOperation(std::function<void(Ts...)>&& func) 
    { 
     static_assert(sizeof...(Ts)==N,"Size of variadic template args does not match array length"); 

     // how can one call func with the entries 
     // of data as the parameters (in a way generic with N) 
    } 

    std::array<T,N> data; 
}; 

int main(void) 
{ 
    Container<3,int> cont(1,2,3); 

    double sum = 0.0; 
    auto func = [&sum](int x, int y, int z)->void{ 
     sum += x; 
     sum += y; 
     sum += z; 
    }; 

    cont.doOperation(std::function<void(int,int,int)>(func)); 

    cout << sum << endl; 

    return 0; 
} 

Так что мой вопрос (как указано в коде), как может один передать записи из data на функцию func таким образом, который является общим с N?

Бонус Вопрос: Можно ли покончить с неприглядным преобразованием в std::function в главном и передать в лямбда прямо?

ответ

11

Учитывая известные индексы инфраструктуры:

namespace detail 
{ 
    template<int... Is> 
    struct seq { }; 

    template<int N, int... Is> 
    struct gen_seq : gen_seq<N - 1, N - 1, Is...> { }; 

    template<int... Is> 
    struct gen_seq<0, Is...> : seq<Is...> { }; 
} 

Вы можете переопределить ваш шаблон класса так:

template<int N, typename T> 
struct Container { 
    template<typename... Ts> 
    Container(Ts&&... vs) : data{{std::forward<Ts>(vs)...}} { 
     static_assert(sizeof...(Ts)==N,"Not enough args supplied!"); 
    } 

    template<typename F> 
    void doOperation(F&& func) 
    { 
     doOperation(std::forward<F>(func), detail::gen_seq<N>()); 
    } 

    template<typename F, int... Is> 
    void doOperation(F&& func, detail::seq<Is...>) 
    { 
     (std::forward<F>(func))(data[Is]...); 
    } 

    std::array<T,N> data; 
}; 

Вот является live example.

Обратите внимание, что вам не нужно создавать объект std::function в main(): std::function может быть неявно построен из лямбда. Тем не менее, вам вообще не нужно использовать std::function, что может привести к ненужным накладным расходам.

В приведенном выше решении я просто позволяю типу вызываемого объекта быть параметром шаблона, который может быть выведен компилятором.

+0

Почему вы экспедиторская 'func' на сайте вызова? – jrok

+2

@jrok: Ну, это в основном теоретическая вещь. Вызываемым объектом может быть rvalue, и он может иметь 'operator() &&'. Очень маловероятно, но все же возможно –

+0

Теперь это тонкое. Ум показывает пример, где это важно? [Я не могу заставить его работать.] (Http://coliru.stacked-crooked.com/view?id=f65a8b7e2330253abae7aab3b75f0fb1-7063104e283ed82d51a6fde7370c6e59) (+1 кстати) – jrok

1

Вы можете использовать эту утилиту шаблон для создания последовательности индексов во время компиляции:

template< std::size_t... Ns > 
struct indices { 
    typedef indices< Ns..., sizeof...(Ns) > next; 
}; 

template< std::size_t N > 
struct make_indices { 
    typedef typename make_indices< N - 1 >::type::next type; 
}; 

template<> 
struct make_indices<0> { 
    typedef indices<> type; 
}; 

Затем сделайте функцию вызывающего абонента, который принимает indices в качестве параметра, чтобы вы получили путь выведения последовательности индексов:

template<typename... Ts, size_t...Is> 
void call(std::function<void(Ts...)>&& func, indices<Is...>) 
{ 
    func(data[Is]...); 
} 

И тогда вы можете назвать это так:

template< typename... Ts> 
void doOperation(std::function<void(Ts...)>&& func) 
{ 
    static_assert(sizeof...(Ts)==N,"Size of variadic template args does not match array length"); 
    call(std::forward<std::function<void(Ts...)>>(func), typename make_indices<N>::type()); 
} 
Смежные вопросы