LoginSignup
3
2

More than 5 years have passed since last update.

C++14 : 任意のタプルへ任意のファンクターを適用する apply( my_tuple, my_functor ) の作り方

Posted at

要求

std::tuple による任意のユーザー定義のタプル TUPLE_TYPE 型の tuple オブジェクトに対し任意のユーザー定義のファンクター FUNCTOR_TYPE 型の functor オブジェクトを適用する apply 関数を作成したい。

期待される使用例

auto tuple = std::make_tuple( 1, 2.3f, '4', "567" );
auto functor = []( auto value ){ std::cout << value << "\n"; };
apply( tuple, functor ); // <-- これを作りたい

期待される結果

この apply に期待される結果として次の出力を得たい。

1
2.3
4
567

そのような apply の作り方

#include <cstddef>
#include <tuple>
#include <utility>

template < typename TUPLE_TYPE, typename FUNCTOR_TYPE, std::size_t ... INDICES >
void apply( TUPLE_TYPE&& tuple, FUNCTOR_TYPE&& functor, std::index_sequence< INDICES ... > )
{
  using swallow = int[];
  (void) swallow { 1, ( functor( std::get< INDICES >( std::forward< TUPLE_TYPE >( tuple ) ) ), void(), int {} ) ... };
}

template < typename TUPLE_TYPE, typename FUNCTOR_TYPE >
void apply( TUPLE_TYPE&& tuple, FUNCTOR_TYPE&& functor )
{
  constexpr std::size_t SIZE = std::tuple_size< std::remove_reference_t< TUPLE_TYPE > >::value;
  apply( std::forward< TUPLE_TYPE >( tuple ), std::forward< FUNCTOR_TYPE >( functor ), std::make_index_sequence< SIZE >{ } );
}

使い方

#include <iostream>

auto main() -> int
{
  apply
  ( std::make_tuple( 1, 2.3f, '4', "567" )
  , []( auto value ) { std::cout << value << "\n"; }
  );
}

実行結果

1
2.3
4
567

Wandbox

ポイント

  • C++14 の機能 : std::make_index_sequence, std::remove_reference_t
  • C++11 の機能 : variadic-templates, r-value-references, constexpr, auto, std::tuple_size, std::forward

トリック

  • appleswallow 型(つまり int[] 型)のブレース初期化により、functor( std::get< INDEX >( tuple ) ) 的な動作について INDEXINDICES ... について一挙に定義する。
    • この時、C++ では void[] 型では扱えないので int[] としている。
      • よって、int 型を作らなくてはならないから swallow で初期化される個々の要素は , int{ } (カンマ演算子+intオブジェクトの生成)を付加し、結果が int 型となるようにしている。
        • このトリックのために INDICES が展開された際に完全に f(), int { } だけでは f()int を返すと困るので間に , void() も挟む。
    • swallow は生成された後にそれ自体は apply の中では使わないのでコンパイラーが warning を出す。そこで (void) にキャストしておくことで不要な警告が出で煩わされないようにしている。
    • 空っぽの int[] { } を定義しないためにダミーで先頭に 1 を入れている。

Reference

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2