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