模板元组 - 在每个元素上调用一个函数

7co*_*ows 37 c++ templates tuples c++11

我的问题在于代码:

template<typename... Ts>
struct TupleOfVectors {
  std::tuple<std::vector<Ts>...> tuple;

  void do_something_to_each_vec() {
    //Question: I want to do this:
    //  "for each (N)": do_something_to_vec<N>()
    //How?
  }

  template<size_t N>
  void do_something_to_vec() {
    auto &vec = std::get<N>(tuple);
    //do something to vec
  }
};
Run Code Online (Sandbox Code Playgroud)

And*_*owl 34

你可以很容易地用一些指数机制做到这一点.给定gen_seq用于生成编译时整数序列的元函数(由seq类模板封装):

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...> { };
}
Run Code Online (Sandbox Code Playgroud)

以及以下功能模板:

#include <tuple>

namespace detail
{
    template<typename T, typename F, int... Is>
    void for_each(T&& t, F f, seq<Is...>)
    {
        auto l = { (f(std::get<Is>(t)), 0)... };
    }
}

template<typename... Ts, typename F>
void for_each_in_tuple(std::tuple<Ts...> const& t, F f)
{
    detail::for_each(t, f, detail::gen_seq<sizeof...(Ts)>());
}
Run Code Online (Sandbox Code Playgroud)

您可以使用for_each_in_tuple以上方式的功能:

#include <string>
#include <iostream>

struct my_functor
{
    template<typename T>
    void operator () (T&& t)
    {
        std::cout << t << std::endl;
    }
};

int main()
{
    std::tuple<int, double, std::string> t(42, 3.14, "Hello World!");
    for_each_in_tuple(t, my_functor());
}
Run Code Online (Sandbox Code Playgroud)

这是一个实例.

在具体情况下,您可以使用它:

template<typename... Ts>
struct TupleOfVectors
{
    std::tuple<std::vector<Ts>...> t;

    void do_something_to_each_vec()
    {
        for_each_in_tuple(t, tuple_vector_functor());
    }

    struct tuple_vector_functor
    {
        template<typename T>
        void operator () (T const &v)
        {
            // Do something on the argument vector...
        }
    };
};
Run Code Online (Sandbox Code Playgroud)

再一次,这是一个现实的例子.

  • 元编程正在吃我的大脑.不确定事情的起点,什么叫什么,最终结果是什么/在哪里.根本不喜欢常规编程.需要一些时间来破译这个:) (9认同)
  • `auto l`允许将右侧的内容作为初始化列表进行处理,这可确保以正确的顺序评估扩展的表达式.`(f(x),0)`使用逗号运算符,以便计算`f(x)`,但是结果值被丢弃,表达式`(f(x),0)`的值是`0`.这是为了给初始化列表的所有元素赋予相同的类型(`int`,here),以便可以推导出初始化列表的类型(`initializer_list <int>`) (5认同)
  • @ 7cows:当然.在开始时它根本不容易,也许这不是最简单的例子,但我确信通过一些练习你很快就能掌握它;) (2认同)
  • 这么多年过去了,第一部分可以用[`std::integer_sequence`](https://en.cppreference.com/w/cpp/utility/integer_sequence)代替吗? (2认同)

M. *_*gan 29

在C++ 17中,您可以这样做:

std::apply([](auto ...x){std::make_tuple(some_function(x)...);} , the_tuple);
Run Code Online (Sandbox Code Playgroud)

鉴于some_function对元组中的所有类型都有适当的重载.

这已经在Clang ++ 3.9中使用了std::experimental::apply.

  • 这是否导致迭代 - 即调用`do_something()` - 以未指定的顺序发生,因为参数包在函数调用`()`中展开,其中参数有未指定的顺序?这可能非常重要; 我想大多数人都希望保证顺序与成员的顺序相同,即作为`std :: get <>()`的索引.AFAIK,为了在这种情况下保证订购,扩展必须在`{braces}`内完成.我错了吗?这个答案强调了这种排序:http://stackoverflow.com/a/16387374/2757035 (5认同)
  • @GuillaumeRacicot 我知道在某些上下文中对评估顺序/保证进行了一些更改,但在函数的参数中没有 - 除了从左到右排序被_考虑_但被拒绝的一些来回。看一下标准的当前草案:https://github.com/cplusplus/draft/blob/master/source/expressions.tex#L1621 `参数的初始化,包括每一个关联值的计算和副作用,是不确定的相对于任何其他参数的顺序。` (3认同)
  • @underscore_d C ++ 17保证函数参数的执行顺序。由于此答案适用于C ++ 17,因此这是有效的。 (2认同)
  • `std :: apply([](auto &amp;&amp; ... x){(static_cast &lt;void&gt;(some_function(std :: forward &lt;decltype(x)&gt;(x)&gt;(x))),...);},the_tuple);以保证评估的顺序,并允许some_function返回void(甚至带有重载的operator的邪恶类)。 (2认同)
  • @ Jarod42,因为我们在这里谈论c ++ 17,所以像`std :: apply([](auto&... x){(...,some_function(x));},the_tuple);的折叠表达式是更好的/sf/answers/3184860241/ (2认同)
  • @DevNull:[Demo](http://coliru.stacked-crooked.com/a/d731bb5ce28a4b76) 邪恶操作符逗号的作用。 (2认同)

Dev*_*ull 15

除了@M 的答案.Alaggan,如果你需要在元组中按顺序调用元组元素,在C++ 17中你也可以使用这样的fold表达式:

std::apply([](auto& ...x){(..., some_function(x));}, the_tuple);
Run Code Online (Sandbox Code Playgroud)

(现场例子).


Vau*_*ato 7

这是一种可能适用于您的情况的方法:

template<typename... Ts>
struct TupleOfVectors {
    std::tuple<std::vector<Ts>...> tuple;

    void do_something_to_each_vec()
    {
        // First template parameter is just a dummy.
        do_something_to_each_vec_helper<0,Ts...>();
    }

    template<size_t N>
    void do_something_to_vec()
    {
        auto &vec = std::get<N>(tuple);
        //do something to vec
    }

private:
    // Anchor for the recursion
    template <int>
    void do_something_to_each_vec_helper() { }

    // Execute the function for each template argument.
    template <int,typename Arg,typename...Args>
    void do_something_to_each_vec_helper()
    {
        do_something_to_each_vec_helper<0,Args...>();
        do_something_to_vec<sizeof...(Args)>();
    }
};
Run Code Online (Sandbox Code Playgroud)

这里唯一有点乱的是额外的虚拟int模板参数do_something_to_each_vec_helper.当没有参数保留时,必须使do_something_to_each_vec_helper仍然是模板.如果您想要使用另一个模板参数,则可以在那里使用它.


Mik*_*han 6

如果您不是特别坚持使用通用"for each"功能模板形式的解决方案,那么您可以使用如下所示:

#ifndef TUPLE_OF_VECTORS_H
#define TUPLE_OF_VECTORS_H

#include <vector>
#include <tuple>
#include <iostream>

template<typename... Ts>
struct TupleOfVectors 
{
    std::tuple<std::vector<Ts>...> tuple;

    template<typename ...Args>
    TupleOfVectors(Args... args)
    : tuple(args...){}

    void do_something_to_each_vec() {
        do_something_to_vec(tuple);
    }

    template<size_t I = 0, class ...P>
    typename std::enable_if<I == sizeof...(P)>::type
    do_something_to_vec(std::tuple<P...> &) {}

    template<size_t I = 0, class ...P>
    typename std::enable_if<I < sizeof...(P)>::type
    do_something_to_vec(std::tuple<P...> & parts) {
        auto & part = std::get<I>(tuple);
        // Doing something...
        std::cout << "vector[" << I << "][0] = " << part[0] << std::endl;
        do_something_to_vec<I + 1>(parts);
    }
};

#endif // EOF
Run Code Online (Sandbox Code Playgroud)

使用GCC 4.7.2和clang 3.2构建的测试程序:

#include "tuple_of_vectors.h"

using namespace std;

int main()
{
    TupleOfVectors<int,int,int,int> vecs(vector<int>(1,1),
        vector<int>(2,2),
        vector<int>(3,3),
        vector<int>(4,4));

    vecs.do_something_to_each_vec();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

相同的递归样式可以在没有辅助索引装置的通用"for_each"函数模板中使用:

#ifndef FOR_EACH_IN_TUPLE_H
#define FOR_EACH_IN_TUPLE_H

#include <type_traits>
#include <tuple>
#include <cstddef>

template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I == sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> &, Func) {}

template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I < sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> & tpl, Func func) 
{
    func(std::get<I>(tpl));
    for_each_in_tuple<I + 1>(tpl,func);
}

#endif //EOF
Run Code Online (Sandbox Code Playgroud)

并为此测试程序:

#include "for_each_in_tuple.h"
#include <iostream>

struct functor
{
    template<typename T>
    void operator () (T&& t)
    {
        std::cout << t << std::endl;
    }
};

int main()
{
    auto tpl = std::make_tuple(1,2.0,"Three");
    for_each_in_tuple(tpl,functor());
    return 0;
}
Run Code Online (Sandbox Code Playgroud)