3

これは、関数の引数としてタプルを展開 (または「展開」) する方法の非常に優れた (私のものではない) 例です。

template<int ...I> struct index_tuple_type {
  template<int N> using append = index_tuple_type<I..., N>;
};

template<int N> struct make_index_impl {
  using type = typename make_index_impl<N-1>::type::template append<N-1>;
};

template<> struct make_index_impl<0> { using type = index_tuple_type<>; };

template<int N> using index_tuple = typename make_index_impl<N>::type;

template <typename I, typename ...Args>
struct func_traits;

template <typename R, int ...I, typename ...Args>
struct func_traits<R, index_tuple_type<I...>, Args...> {
  template <typename TT, typename FT>
  static inline R call(TT &&t, FT &&f) {
    return f(std::get<I>(std::forward<TT>(t))...);
  }
};

template<
  typename FT,
  typename ...Args, 
  typename R = typename std::result_of<FT(Args&&...)>::type
>
inline R explode(std::tuple<Args...>& t, FT &&f) {
  return func_traits<R, index_tuple<sizeof...(Args)>, Args...>
    ::call(t, std::forward<FT>(f));
}

次に、これを次のように使用できます。

void test1(int i, char c) {
  printf("%d %c\n", i, c);
}

int main() {
  std::tuple<int, char> t1{57, 'a'};
  explode(t1, test1);
}

ライブバージョン

std::arrayタプルにとても似ているので、どうやって同じことをすることができるかをさまよっていました。std::get<N>で動作するstd::arrayので、このソリューションを変更するのは簡単だと思いました。しかし、このようなものは機能しません:

template<
  typename FT,
  typename Arg,
  std::size_t I,
  typename R = typename std::result_of<FT(Arg&&)>::type
>
inline R explode(std::array<Arg, I>& t, FT &&f) {
  return func_traits<R, index_tuple<I>, Arg>::
    call(t, std::forward<FT>(f));
}

void test2(int i1, int i2) {
  printf("%d %d\n", i1, i2);
}

int main() {
  std::array<int, int> t1{1, 2};
  explode(t2, test1);
}

パーツの為お譲り致しstd::result_of<FT(Arg&&)>::typeます。引数の型Arg&&が間違ってresult_ofいて、フィールドがありませんtype。タプルArgs&&...が拡張されましたが、今では「繰り返される」必要がありますIresult_of返された型を差し引くことができるようにこれを行う方法はありますか?

また、「アンパック」するツールがあり、tuple再帰的に(おそらく を使用して)構造などarrayを「アンパック」することは可能でしょうか?とが枝で、他の種類が葉であるある種の木?enable_iftuple<array<int, 2>, tuple<array<double,3>, ...tuplearray

4

1 に答える 1

8
// enable argument dependent lookup on `get` call:
namespace aux {
  using std::get;
  template<size_t N, class T>
  auto adl_get( T&& )->decltype( get<N>(std::declval<T>()) );
}
using aux::adl_get;
template<class F, class TupleLike, size_t...Is>
auto explode( F&& f, TupleLike&& tup, std::index_sequence<Is...> )
-> std::result_of_t< F( decltype(adl_get<Is>(std::forward<TupleLike>(tup)))... ) >
{
  using std::get; // ADL support
  return std::forward<F>(f)( get<Is>(std::forward<TupleLike>(tup))... );
}

最初のステップです。 std::index_sequenceは C++14 ですが、C++11 での実装は簡単です。

次のステップも簡単です。

まず、どのタイプがタプルに似ているかを指示する特性クラス。私は先に進み、それらをダック型で使用しますが、使用する多くの関数と特性クラスは SFINAE フレンドリーではありません:

template<class T>
struct tuple_like:std::false_type{};
template<class... Ts>
struct tuple_like<std::tuple<Ts...>>:std::true_type{};
template<class... Ts>
struct tuple_like<std::pair<Ts...>>:std::true_type{};
template<class T, size_t N>
struct tuple_like<std::array<T,N>>:std::true_type{};

次に、型explodeでのみ機能する のオーバーロードtuple_like:

template<class F, class TupleLike,
  class TupleType=std::decay_t<TupleLike>, // helper type
  class=std::enable_if_t<tuple_like<TupleType>{}>> // SFINAE tuple_like test
auto explode( F&& f, TupleLike&& tup )
-> decltype(
  explode(
    std::declval<F>(),
    std::declval<TupleLike>(), 
    std::make_index_sequence<std::tuple_size<TupleType>{}>{}
  )
)
{
   using indexes = std::make_index_sequence<std::tuple_size<TupleType>{}>;
   return explode(
     std::forward<F>(f),
     std::forward<TupleLike>(tup),
     indexes{}
   );
}

サポートが不足している場合は、一部をconstexprに変更する必要があります。{}::value

上記は、ペア、配列、またはタプルのトリックを行います。他のタプルのような型のサポートを追加する場合は、単に特殊化を追加して、型に適切に特殊化され、 (型を囲む名前空間で) ADL オーバーロードされていることをtuple_like確認します。std::tuple_sizeget<N>


std::make_index_sequenceも C++14 ですが、C++11 で簡単に記述できます。

template<size_t...>
struct index_sequence{};
namespace details {
  template<size_t count, size_t...Is>
  struct mis_helper:mis_helper<count-1, count-1, Is...> {};
  template<size_t...Is>
  struct mis_helper<0,Is...> {
    using type=index_sequence<Is...>;
  };
}
template<size_t count>
using make_index_sequence=typename details::mis_helper<count>::type;

(これは、サイズ n のリストに対して O(n) テンプレートの再帰テンプレートのインスタンス化を必要とするため、少なくとも対数降下を使用する必要がある C++14 ライブラリの QOI が低いです。ただし、n は数 100 未満です。関係ありません)。

std::enable_if_t<?>は C++14 ですが、C++11 ではtypename std::enable_if<?>::type.

于 2015-02-10T19:42:06.153 に答える