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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16387354/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 20:19:00  来源:igfitidea点击:

Template tuple - calling a function on each element

c++templatesc++11tuples

提问by 7cows

My question is in the code:

我的问题是在代码中:

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

采纳答案by Andy Prowl

You can quite easily do that with some indices machinery. Given a meta-function gen_seqfor generating compile-time integer sequences (encapsulated by the seqclass template):

你可以很容易地用一些索引机制来做到这一点。给定一个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...> { };
}

And the following function templates:

以及以下功能模板:

#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)>());
}

You can use the for_each_in_tuplefunction above this way:

你可以这样使用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());
}

Here is a live example.

这是一个活生生的例子

In your concrete situation, this is how you could use it:

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

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

And once again, here is a live example.

再一次,这是一个活生生的例子

回答by Mohammad Alaggan

In C++17 you can do this:

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

std::apply([](auto ...x){std::make_tuple(some_function(x)...);} , the_tuple);

given that some_functionhas suitable overloads for all the types in the tuple.

鉴于some_function它对元组中的所有类型都有合适的重载。

This already works in Clang++ 3.9, using std::experimental::apply.

这已经适用于 Clang++ 3.9,使用std::experimental::apply.

回答by Dev Null

In addition to the answerof @M. Alaggan, if you need to call a function on tuple elements in order of their appearance?in the tuple, in C++17 you can also use a fold expression like this:

除了@M的回答。Alaggan,如果您需要按元组元素的出现顺序在元组元素上调用函数在元组中,在 C++17 中,您还可以使用这样的折叠表达式:

std::apply([](auto& ...x){(..., some_function(x));}, the_tuple);

(live example).

活生生的例子)。

?Because otherwise order of evaluation of function arguments is unspecified.

? 因为否则函数参数的评估顺序是未指定的

回答by Vaughn Cato

Here's one approach which may work well in your case:

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

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

The only thing that is a bit messy here is the extra dummy inttemplate parameter to do_something_to_each_vec_helper. It is necessary to make the do_something_to_each_vec_helper still be a template when no arguments remain. If you had another template parameter you wanted to use, you could use it there instead.

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

回答by Mike Kinghan

If you are not particularly wedded to a solution in the form of generic "for each" function template then you can use one like this:

如果您不是特别热衷于通用“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

A test program, built with GCC 4.7.2 and clang 3.2:

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

The same style of recursion can be used in a generic "for_each" function template without auxiliary indices apparatus:

可以在没有辅助索引装置的通用“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

And a test program for that:

和一个测试程序:

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

回答by dmayola

I was testing with tuples and metaprograming and found the current thread. I think my work can inspire someone else although I like the solution of @Andy.

我正在使用元组和元编程进行测试,并找到了当前线程。虽然我喜欢@Andy 的解决方案,但我认为我的工作可以激励其他人。

Anyway, just get fun!

无论如何,尽情享受吧!

#include <tuple>
#include <type_traits>
#include <iostream>
#include <sstream>
#include <functional>

template<std::size_t I = 0, typename Tuple, typename Func>
typename std::enable_if< I != std::tuple_size<Tuple>::value, void >::type
for_each(const Tuple& tuple, Func&& func)
{
    func(std::get<I>(tuple));
    for_each<I + 1>(tuple, func);
}

template<std::size_t I = 0, typename Tuple, typename Func>
typename std::enable_if< I == std::tuple_size<Tuple>::value, void >::type
for_each(const Tuple& tuple, Func&& func)
{
    // do nothing
}


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

template<typename... Params>
void test(Params&& ... params)
{
    int sz = sizeof...(params);
    std::tuple<Params...> values(std::forward<Params>(params)...);
    for_each(values, print() );
}


class MyClass
{
public:
    MyClass(const std::string& text) 
        : m_text(text)
    {
    }

    friend std::ostream& operator <<(std::ostream& stream, const MyClass& myClass)
    {
        stream << myClass.m_text;
        return stream;
    }

private:
    std::string m_text;
};


int main()
{
    test(1, "hello", 3.f, 4, MyClass("I don't care") );
}

回答by NoSenseEtAl

Boost mp11 has this functionality:

Boost mp11 具有以下功能:

#include <iostream>
#include <string>
#include <boost/mp11.hpp>

using namespace std;
using boost::mp11::tuple_for_each;

std::tuple t{string("abc"), 47 };

int main(){
    tuple_for_each(t,[](const auto& x){
        cout << x + x << endl;
    });
}