web-dev-qa-db-ja.com

テンプレートタプル-各要素で関数を呼び出す

私の質問はコードにあります:

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

いくつかのインデックスマシンを使用すると、非常に簡単にこれを行うことができます。コンパイル時整数シーケンスを生成するためのメタ関数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...> { };
}

そして、次の関数テンプレート:

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

この方法で上記の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());
}

ライブの例 です。

具体的な状況では、次のように使用できます。

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

もう一度、ここに live example があります。

34
Andy Prowl

C++ 17では、これを行うことができます。

std::apply([](auto ...x){std::make_Tuple(some_function(x)...);} , the_Tuple);

some_functionがタプル内のすべてのタイプに適したオーバーロードを持っていると仮定します。

これは、std::experimental::applyを使用して、Clang ++ 3.9で既に機能しています。

28
M. Alaggan

@ -Mの 答え に加えてAlaggan、タプル要素の関数をその出現順に呼び出す必要がある場合 Tupleでは、C++ 17では、次のようなfold式も使用できます。

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

live example )).

それ以外の場合 関数の引数の評価の順序は指定されていない

13
Dev Null

あなたの場合にうまくいくかもしれない1つのアプローチがあります:

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

ここで少し厄介なのは、do_something_to_each_vec_helperへの追加のダミーintテンプレートパラメータのみです。引数が残っていない場合、do_something_to_each_vec_helperをテンプレートのままにする必要があります。使用したい別のテンプレートパラメータがある場合は、代わりにそこで使用できます。

7
Vaughn Cato

汎用の「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

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

同じスタイルの再帰を、補助インデックス装置なしで汎用の「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

そしてそのためのテストプログラム:

#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;
}
6
Mike Kinghan

タプルとメタプログラミングをテストしていて、現在のスレッドを見つけました。 @Andyのソリューションは気に入っていますが、私の仕事は他の誰かに刺激を与えることができると思います。

とにかく、楽しんでください!

#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") );
}
2
dmayola