将std::array作为模板可变参数函数的参数传递

11
我正在尝试学习C++11中的可变参数模板。我有一个类,基本上是std::array的包装器。我想能够将函数对象(理想情况下是lambda)传递给成员函数,然后将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;
}

所以我的问题(如代码中所示)是如何以一种通用于 N 的方式将 data 的条目传递到函数 func 中?

奖励问题:是否可以摆脱在主函数中进行的不美观的转换,并直接传递lambda表达式?

2个回答

15

考虑到众所周知的指数基础设施:

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

这里有一个实时示例

请注意,您不需要在main()中构造一个std::function对象:可以从lambda隐式构造std::function。然而,在这里甚至不需要使用std::function,可能会产生不必要的运行时开销。

在上面的解决方案中,我只让可调用对象的类型成为可以由编译器推导出的模板参数。


你为什么在调用处转发 func - jrok
3
@jrok说:嗯,这主要是一个理论性的问题。可调用对象可以是一个rvalue,并且它可以有一个 operator () &&。很不可能,但仍然有可能。 - Andy Prowl
有没有与 std 中的 indices 相当的东西?您是否知道一个解释它如何工作的链接?顺便说一句,感谢您的回答。 - Dan
@Dan:jrok 可能指的是我在评论中提供的链接,而不是答案中的链接。 - Andy Prowl
1
@Dan:是的,它是一种类型,并且函数中并不需要所有参数都有名称(只要你不使用它们)。在这种情况下,我不需要在函数内部使用该参数:该参数只是为了允许在类型推导期间推断出“Is”,以便我可以基于它创建模式(如data[Is])并展开它。关于你的例子,不,你不能那样做,包扩展只能在某些上下文中使用,而那不是一个合法的上下文。 - Andy Prowl
显示剩余10条评论

2
您可以使用此实用程序模板在编译时创建索引序列:
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() );
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接