Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
651 views
in Technique[技术] by (71.8m points)

c++ - 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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...