7

任意のタプルの各要素に対して、テンプレートまたはオーバーロードされた関数を呼び出す必要があります。正確には、タプルで指定されている要素に対してこの関数を呼び出す必要があります。

例えば。私はタプルstd::tuple<int, float> t{1, 2.0f};と関数を持っています

class Lambda{
public: 
   template<class T>
   void operator()(T arg){ std::cout << arg << "; "; }
};

struct/function が必要ですApply。これを呼び出すと、次のようApply<Lambda, int, float>()(Lambda(), t)になります。

1; 2.0f; 

ではない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));
}

コードはコンパイルされますが、最初の引数のみが出力されます。ただし、ForwardsApplicator専門分野を次のように置き換えると、

template<class Func, class... Components>
class ForwardsApplicator < Func, 2, Components... >{...}

それは正しく動作します - もちろん、長さ 2 のタプルに対してのみです。可能であれば、エレガントに - 任意の長さのタプルに対してどのように行うのですか?

編集:答えてくれてありがとう!3 つすべてが本当に的を射ており、考えられるすべての観点から問題を説明しています。

4

3 に答える 3

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

デモ

std::index_sequence友達は C++14 ですが、これは純粋なライブラリ拡張であり、C++11 で簡単に実装できます。SO では、6 つの実装を簡単に見つけることができます。

于 2015-02-05T12:46:36.077 に答える
2

テンプレートでのカウント ダウンは、タプル要素を同じ順序で処理することを意味する必要はありません。タプルを最初から最後まで処理する簡単な方法は、(末尾の再帰ではなく) 先頭の再帰です。

#include <tuple>
#include <iostream>

// Our entry point is the tuple size
template<typename Tuple, std::size_t i = std::tuple_size<Tuple>::value>
struct tuple_applicator {
  template<typename Func> static void apply(Tuple &t, Func &&f) {
    // but we recurse before we process the element associated with that
    // number, reversing the order so that the front elements are processed
    // first.
    tuple_applicator<Tuple, i - 1>::apply(t, std::forward<Func>(f));
    std::forward<Func>(f)(std::get<i - 1>(t));
  }
};

// The recursion stops here.
template<typename Tuple>
struct tuple_applicator<Tuple, 0> {
  template<typename Func> static void apply(Tuple &, Func &&) { }
};

// and this is syntactical sugar
template<typename Tuple, typename Func>
void tuple_apply(Tuple &t, Func &&f) {
  tuple_applicator<Tuple>::apply(t, std::forward<Func>(f));
}

int main() {
  std::tuple<int, double> t { 1, 2.3 };

  // The generic lambda requires C++14, the rest
  // works with C++11 as well. Put your Lambda here instead.
  tuple_apply(t, [](auto x) { std::cout << x * 2 << '\n'; });
}

出力は

2
4.6
于 2015-02-05T12:29:22.683 に答える