以自然(非反向)顺序将func应用于std :: tuple中的元素

Mis*_*cha 7 c++ functional-programming c++11 stdtuple

我需要为任意元组中的每个元素调用 - template或overloaded函数.确切地说,我需要在元组中指定元素,因为它们在元组中指定.

例如.我有一个元组std::tuple<int, float> t{1, 2.0f};和一个功能

class Lambda{
public: 
   template<class T>
   void operator()(T arg){ std::cout << arg << "; "; }
};
Run Code Online (Sandbox Code Playgroud)

我需要一些结构/函数Apply,如果调用它Apply<Lambda, int, float>()(Lambda(), t)会产生:

1; 2.0f; 
Run Code Online (Sandbox Code Playgroud)

而不是2.0f; 1;.

请注意,我知道解决方案,如果将"原始"参数包传递给函数,并且我知道如何以相反的顺序对元组执行此操作.但是以下部分特化的尝试Apply失败了:

template<class Func, size_t index, class ...Components>
class ForwardsApplicator{
public:
    void operator()(Func func, const std::tuple<Components...>& t){
        func(std::get<index>(t));
        ForwardsApplicator<Func, index + 1, Components...>()(func, t);
    }
};

template<class Func, class... Components>
class ForwardsApplicator < Func, sizeof...(Components), Components... > {
public:
    void operator()(Func func, const std::tuple<Components...>& t){}
};

int main{
    ForwardsApplicator<Lambda, 0, int, float>()(Lambda{}, std::make_tuple(1, 2.0f));
}
Run Code Online (Sandbox Code Playgroud)

编译代码但只打印第一个参数.但是,如果我用ForwardsApplicator专业化替换

template<class Func, class... Components>
class ForwardsApplicator < Func, 2, Components... >{...}
Run Code Online (Sandbox Code Playgroud)

它工作正常 - 但是,当然,仅适用于长度为2的元组.如果可能,优雅地为任意长度的元组做什么?

编辑:谢谢你们的答案!这三个都是非常直接的,并从所有可能的有利位置解释问题.

T.C*_*.C. 9

这是一个教科书案例integer_sequence.

template<class Func, class Tuple, size_t...Is>
void for_each_in_tuple(Func f, Tuple&& tuple, std::index_sequence<Is...>){
    using expander = int[];
    (void)expander { 0, ((void)f(std::get<Is>(std::forward<Tuple>(tuple))), 0)... };
}

template<class Func, class Tuple>
void for_each_in_tuple(Func f, Tuple&& tuple){
    for_each_in_tuple(f, std::forward<Tuple>(tuple),
               std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>());
}
Run Code Online (Sandbox Code Playgroud)

演示.

std::index_sequence和朋友是C++ 14,但它是一个纯库扩展,可以很容易地在C++ 11中实现.您可以在SO上轻松找到六个实现.


lee*_*mes 6

问题是size...(Components)不能用于未知类型列表的专门化Components.GCC抱怨错误:

prog.cpp:16:7: error: template argument 'sizeof... (Components)' involves template parameter(s)
 class ForwardsApplicator < Func, sizeof...(Components), Components... > {
       ^
Run Code Online (Sandbox Code Playgroud)

我建议采用略有不同的方法.首先,将类型列表移动Components到模板参数中operator(),即:

template<class ...Components>
void operator()(Func func, const std::tuple<Components...>& t) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

然后,反转调用顺序:首先执行递归调用,然后执行调用index-1(即调用最后一个元组元素).开始这个递归,index = sizeof...(Components)直到index = 0哪个是noop(所以专业化0,独立sizeof...(Components)于我开始讨论的问题).

要帮助调用此方法,请添加模板参数推导功能:

// General case (recursion)
template<class Func, size_t index>
class ForwardsApplicator{
public:
    template<class ...Components>
    void operator()(Func func, const std::tuple<Components...>& t){
        ForwardsApplicator<Func, index - 1>()(func, t);
        func(std::get<index - 1>(t));
    }
};

// Special case (stop recursion)
template<class Func>
class ForwardsApplicator<Func, 0> {
public:
    template<class ...Components>
    void operator()(Func func, const std::tuple<Components...>& t){}
};

// Helper function for template type deduction
template<class Func, class ...Components>
void apply(Func func, const std::tuple<Components...>& t) {
    ForwardsApplicator<Func, sizeof...(Components)>()(func, t);
}
Run Code Online (Sandbox Code Playgroud)

然后很容易调用,而无需在调用站点上使用任何模板参数:

apply(Lambda{}, std::make_tuple(1, 2.0f));
Run Code Online (Sandbox Code Playgroud)

现场演示