简体   繁体   中英

Passing std::array as arguments of template variadic function

I am trying to learn about variadic templates in C++11. I have a class which is basically a wrapper around a std::array . I want to be able to pass function objects (ideally lambdas) to a member function and then have the elements of the std::array passed on as parameters of the function object.

I have used a static_assert to check that the number of parameters matches the length of the array but I cannot think of a way to pass the elements as arguments.

Here is the code

#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;
}

So my question (as indicated in the code) is how can one pass the entries of data onto the function func in a way which is generic with N ?

Bonus Question: Is it possible to do away with the unsightly conversion to std::function in main and pass in a lambda directly?

Given the well-known indices infrastructure:

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...> { };
}

You could redefine your class template this way:

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;
};

Here is a live example .

Notice, that you do not need to construct an std::function object in main() : the std::function can be implicitly constructed from the lambda. However, you do not even need to use std::function at all here, possibly incurring in an unnecessary run-time overhead.

In the solution above, I just let the type of the callable object to be a template parameter that can be deduced by the compiler.

You can use this utility template to create a sequence of indices at compile time:

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;
};

Then make a caller function that takes indices as a parameter so you got a way of deducing the indices sequence:

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

And then you can call it like this:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM