137

(C++11 を使用して) タプルを反復処理するにはどうすればよいですか? 私は次のことを試しました:

for(int i=0; i<std::tuple_size<T...>::value; ++i) 
  std::get<i>(my_tuple).do_sth();

しかし、これは機能しません:

エラー 1: 申し訳ありませんが、実装されていません: 'Listener ...' を固定長の引数リストに展開できません。
エラー 2: i は定数式には使用できません。

では、タプルの要素を正しく反復処理するにはどうすればよいでしょうか?

4

20 に答える 20

140

タプルの繰り返しに基づく答えがあります:

#include <tuple>
#include <utility> 
#include <iostream>

template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
  print(std::tuple<Tp...>& t)
  { }

template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
  print(std::tuple<Tp...>& t)
  {
    std::cout << std::get<I>(t) << std::endl;
    print<I + 1, Tp...>(t);
  }

int
main()
{
  typedef std::tuple<int, float, double> T;
  T t = std::make_tuple(2, 3.14159F, 2345.678);

  print(t);
}

通常のアイデアは、コンパイル時の再帰を使用することです。実際、このアイデアは、元のタプル ペーパーに記載されているように、タイプ セーフな printf を作成するために使用されます。

for_eachこれはfor タプルに簡単に一般化できます。

#include <tuple>
#include <utility> 

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
  for_each(std::tuple<Tp...> &, FuncT) // Unused arguments are given no names.
  { }

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
  for_each(std::tuple<Tp...>& t, FuncT f)
  {
    f(std::get<I>(t));
    for_each<I + 1, FuncT, Tp...>(t, f);
  }

FuncTただし、これには、タプルに含まれる可能性のあるすべての型に対して適切なオーバーロードを使用して何かを表現するための努力が必要です。これは、すべてのタプル要素が共通の基本クラスまたは類似のものを共有することがわかっている場合に最適です。

于 2011-08-01T05:15:34.287 に答える
28

Boost.Fusionは可能性です:

テストされていない例:

struct DoSomething
{
    template<typename T>
    void operator()(T& t) const
    {
        t.do_sth();
    }
};

tuple<....> t = ...;
boost::fusion::for_each(t, DoSomething());
于 2009-07-29T17:41:30.060 に答える
26

C++17 では、次のことができます。

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

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

于 2016-05-08T13:05:23.253 に答える
23

Boost.Hana とジェネリック ラムダを使用します。

#include <tuple>
#include <iostream>
#include <boost/hana.hpp>
#include <boost/hana/ext/std/tuple.hpp>

struct Foo1 {
    int foo() const { return 42; }
};

struct Foo2 {
    int bar = 0;
    int foo() { bar = 24; return bar; }
};

int main() {
    using namespace std;
    using boost::hana::for_each;

    Foo1 foo1;
    Foo2 foo2;

    for_each(tie(foo1, foo2), [](auto &foo) {
        cout << foo.foo() << endl;
    });

    cout << "foo2.bar after mutation: " << foo2.bar << endl;
}

http://coliru.stacked-crooked.com/a/27b3691f55caf271

于 2014-04-17T20:21:24.473 に答える
8

最初にいくつかのインデックス ヘルパーを定義します。

template <size_t ...I>
struct index_sequence {};

template <size_t N, size_t ...I>
struct make_index_sequence : public make_index_sequence<N - 1, N - 1, I...> {};

template <size_t ...I>
struct make_index_sequence<0, I...> : public index_sequence<I...> {};

あなたの関数では、各タプル要素に適用したいと思います:

template <typename T>
/* ... */ foo(T t) { /* ... */ }

あなたは書ける:

template<typename ...T, size_t ...I>
/* ... */ do_foo_helper(std::tuple<T...> &ts, index_sequence<I...>) {
    std::tie(foo(std::get<I>(ts)) ...);
}

template <typename ...T>
/* ... */ do_foo(std::tuple<T...> &ts) {
    return do_foo_helper(ts, make_index_sequence<sizeof...(T)>());
}

または、をfoo返す場合はvoid、使用します

std::tie((foo(std::get<I>(ts)), 1) ... );

注: C++14make_index_sequenceでは既に定義されています ( http://en.cppreference.com/w/cpp/utility/integer_sequence )。

左から右への評価順序が必要な場合は、次のように考えてください。

template <typename T, typename ...R>
void do_foo_iter(T t, R ...r) {
    foo(t);
    do_foo(r...);
}

void do_foo_iter() {}

template<typename ...T, size_t ...I>
void do_foo_helper(std::tuple<T...> &ts, index_sequence<I...>) {
    do_foo_iter(std::get<I>(ts) ...);
}

template <typename ...T>
void do_foo(std::tuple<T...> &ts) {
    do_foo_helper(ts, make_index_sequence<sizeof...(T)>());
}
于 2014-06-30T00:06:55.770 に答える
8

Boost.Tuple で示されているように、テンプレート メタプログラミングを使用する必要があります。

#include <boost/tuple/tuple.hpp>
#include <iostream>

template <typename T_Tuple, size_t size>
struct print_tuple_helper {
    static std::ostream & print( std::ostream & s, const T_Tuple & t ) {
        return print_tuple_helper<T_Tuple,size-1>::print( s, t ) << boost::get<size-1>( t );
    }
};

template <typename T_Tuple>
struct print_tuple_helper<T_Tuple,0> {
    static std::ostream & print( std::ostream & s, const T_Tuple & ) {
        return s;
    }
};

template <typename T_Tuple>
std::ostream & print_tuple( std::ostream & s, const T_Tuple & t ) {
    return print_tuple_helper<T_Tuple,boost::tuples::length<T_Tuple>::value>::print( s, t );
}

int main() {

    const boost::tuple<int,char,float,char,double> t( 0, ' ', 2.5f, '\n', 3.1416 );
    print_tuple( std::cout, t );

    return 0;
}

print_tuple()C++0x では、代わりに可変個引数テンプレート関数として記述できます。

于 2009-07-29T06:54:25.837 に答える
5

std::tuple を使用したい場合で、可変個引数テンプレートをサポートする C++ コンパイラがある場合は、次のコードを試してください (g++4.5 でテスト済み)。これがあなたの質問に対する答えになるはずです。

#include <tuple>

// ------------- UTILITY---------------
template<int...> struct index_tuple{}; 

template<int I, typename IndexTuple, typename... Types> 
struct make_indexes_impl; 

template<int I, int... Indexes, typename T, typename ... Types> 
struct make_indexes_impl<I, index_tuple<Indexes...>, T, Types...> 
{ 
    typedef typename make_indexes_impl<I + 1, index_tuple<Indexes..., I>, Types...>::type type; 
}; 

template<int I, int... Indexes> 
struct make_indexes_impl<I, index_tuple<Indexes...> > 
{ 
    typedef index_tuple<Indexes...> type; 
}; 

template<typename ... Types> 
struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...> 
{}; 

// ----------- FOR EACH -----------------
template<typename Func, typename Last>
void for_each_impl(Func&& f, Last&& last)
{
    f(last);
}

template<typename Func, typename First, typename ... Rest>
void for_each_impl(Func&& f, First&& first, Rest&&...rest) 
{
    f(first);
    for_each_impl( std::forward<Func>(f), rest...);
}

template<typename Func, int ... Indexes, typename ... Args>
void for_each_helper( Func&& f, index_tuple<Indexes...>, std::tuple<Args...>&& tup)
{
    for_each_impl( std::forward<Func>(f), std::forward<Args>(std::get<Indexes>(tup))...);
}

template<typename Func, typename ... Args>
void for_each( std::tuple<Args...>& tup, Func&& f)
{
   for_each_helper(std::forward<Func>(f), 
                   typename make_indexes<Args...>::type(), 
                   std::forward<std::tuple<Args...>>(tup) );
}

template<typename Func, typename ... Args>
void for_each( std::tuple<Args...>&& tup, Func&& f)
{
   for_each_helper(std::forward<Func>(f), 
                   typename make_indexes<Args...>::type(), 
                   std::forward<std::tuple<Args...>>(tup) );
}

boost::fusion も別のオプションですが、独自のタプル タイプ、boost::fusion::tuple が必要です。標準に固執しましょう!ここにテストがあります:

#include <iostream>

// ---------- FUNCTOR ----------
struct Functor 
{
    template<typename T>
    void operator()(T& t) const { std::cout << t << std::endl; }
};

int main()
{
    for_each( std::make_tuple(2, 0.6, 'c'), Functor() );
    return 0;
}

可変個引数テンプレートの力!

于 2011-06-19T09:39:11.100 に答える
2

他の人は、よく設計されたサードパーティのライブラリについて言及しています。ただし、これらのサードパーティ ライブラリなしで C++ を使用している場合は、次のコードが役立つ場合があります。

namespace detail {

template <class Tuple, std::size_t I, class = void>
struct for_each_in_tuple_helper {
  template <class UnaryFunction>
  static void apply(Tuple&& tp, UnaryFunction& f) {
    f(std::get<I>(std::forward<Tuple>(tp)));
    for_each_in_tuple_helper<Tuple, I + 1u>::apply(std::forward<Tuple>(tp), f);
  }
};

template <class Tuple, std::size_t I>
struct for_each_in_tuple_helper<Tuple, I, typename std::enable_if<
    I == std::tuple_size<typename std::decay<Tuple>::type>::value>::type> {
  template <class UnaryFunction>
  static void apply(Tuple&&, UnaryFunction&) {}
};

}  // namespace detail

template <class Tuple, class UnaryFunction>
UnaryFunction for_each_in_tuple(Tuple&& tp, UnaryFunction f) {
  detail::for_each_in_tuple_helper<Tuple, 0u>
      ::apply(std::forward<Tuple>(tp), f);
  return std::move(f);
}

注: コードは、C++11 をサポートする任意のコンパイラでコンパイルされ、標準ライブラリの設計との一貫性を保ちます。

  1. タプルは である必要はなく、代わりにandstd::tupleをサポートするものであれば何でもかまいません。特に、使用される可能性があります。std::getstd::tuple_sizestd::arraystd::pair

  2. タプルは、参照型または cv 修飾されたものである場合があります。

  3. と同様の動作をしstd::for_each、入力を返しますUnaryFunction

  4. C++14 (または最新バージョン) のユーザーの場合typename std::enable_if<T>::type、簡略化されたバージョンにtypename std::decay<T>::type置き換えることができます。std::enable_if_t<T>std::decay_t<T>

  5. C++17 (またはそれ以降のバージョン) のユーザーの場合、std::tuple_size<T>::valueはその簡略化されたバージョンに置き換えることができますstd::tuple_size_v<T>

  6. C++20 (または最新バージョン) のユーザーの場合、このSFINAE機能はConcepts.

于 2019-01-05T11:05:15.757 に答える
1

@Stypoxの回答を拡張すると、ソリューションをより一般的なものにすることができます(C++ 17以降)。呼び出し可能な関数の引数を追加することにより:

template<size_t I = 0, typename... Tp, typename F>
void for_each_apply(std::tuple<Tp...>& t, F &&f) {
    f(std::get<I>(t));
    if constexpr(I+1 != sizeof...(Tp)) {
        for_each_apply<I+1>(t, std::forward<F>(f));
    }
}

次に、各タイプを訪問するための戦略が必要です。

いくつかのヘルパーから始めましょう (最初の 2 つは cppreference から取得):

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
template<class ... Ts> struct variant_ref { using type = std::variant<std::reference_wrapper<Ts>...>; };

variant_refタプルの状態を変更できるようにするために使用されます。

使用法:

std::tuple<Foo, Bar, Foo> tuples;

for_each_apply(tuples,
               [](variant_ref<Foo, Bar>::type &&v) {
                   std::visit(overloaded {
                       [](Foo &arg) { arg.foo(); },
                       [](Bar const &arg) { arg.bar(); },
                   }, v);
               });

結果:

Foo0
Bar
Foo0
Foo1
Bar
Foo1

完全を期すために、ここに私のBar&がありFooます:

struct Foo {
    void foo() {std::cout << "Foo" << i++ << std::endl;}
    int i = 0;
};
struct Bar {
    void bar() const {std::cout << "Bar" << std::endl;}
};
于 2020-05-31T10:25:36.273 に答える
-1

boost のタプルはヘルパー関数get_head()を提供するget_tail()ため、ヘルパー関数は次のようになります。

inline void call_do_sth(const null_type&) {};

template <class H, class T>
inline void call_do_sth(cons<H, T>& x) { x.get_head().do_sth(); call_do_sth(x.get_tail()); }

ここで説明されているようにhttp://www.boost.org/doc/libs/1_34_0/libs/tuple/doc/tuple_advanced_interface.html

それと似てstd::tupleいるはずです。

実際には、残念ながらそのようなインターフェイスを提供していないようです。そのため、以前に提案されたメソッドが機能するか、他の利点 (既に提供されている io 演算子など) を持つstd::tupleメソッドに切り替える必要があります。boost::tuplegccには欠点がありますがboost::tuple、可変個引数のテンプレートはまだ受け付けていませんが、マシンに最新バージョンのブーストがインストールされていないため、既に修正されている可能性があります。

于 2010-08-26T21:27:06.513 に答える