【问题标题】:Passing std::array as arguments of template variadic function将 std::array 作为模板可变参数函数的参数传递
【发布时间】:2013-05-25 22:49:08
【问题描述】:

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

额外问题:是否可以在 main 中取消难看的转换为 std::function 并直接传入 lambda?

【问题讨论】:

    标签: c++ templates c++11 variadic-templates


    【解决方案1】:

    鉴于众所周知的索引基础设施:

    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

    注意,您不需要在 main() 中构造 std::function 对象:std::function 可以从 lambda 隐式构造。但是,您甚至根本不需要在这里使用std::function,这可能会导致不必要的运行时开销。

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

    【讨论】:

    • 你为什么要在呼叫现场转发func
    • @jrok:嗯,这主要是理论上的事情。可调用对象可以是一个右值,它可以有一个operator () &amp;&amp;。不太可能,但仍有可能
    • 现在很微妙。介意举一个重要的例子吗? I can't get it to work.(+1 顺便说一句)
    • @jrok:那是因为只有 Clang 支持对 thisAFAIK 的引用。 Here 就是一个例子
    • @Dan:它是一种类型,是的,并不是函数中的所有参数都需要有名称(只要你不使用它们)。在这种情况下,我不需要在函数内部使用该参数:该参数只是为了在类型推断期间允许推断Is,以便我可以基于它创建一个模式(如data[Is])并展开它。关于您的示例,不,您不能这样做,包扩展只能在某些情况下使用,这不是合法的
    【解决方案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() );
    }
    

    【讨论】:

      猜你喜欢
      • 2014-10-20
      • 2014-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-24
      • 2015-09-22
      • 1970-01-01
      • 2023-03-10
      相关资源
      最近更新 更多