はじめに

この記事では、ある要求に対して、筆者が気づいた1つの解決策を載せています。もっと拡張できる!や、全く別のアプローチがある!などの発見をぜひ楽しんでください。

要求

同一の型を格納したコレクションに対して、そのすべての要素に関数を適用したい。

コレクション

タイトルにあるように、サンプルとなるコレクションをstd::tuple で定義してみる。

enum class color { r, g, b };
const auto list = std::make_tuple( 
    color::r, 
    color::g, 
    color::b );

std::array のインスタンス化には必要な要素数の明示的な記述は、std::tuple では必要ない。これは、プログラムの拡張性を維持したい場合に嬉しい性質である。また、要素へのアクセスにはアクセサ(std::get)を通すため、不正な操作を防止しやすい。

実装

早速、map “風”の関数適用の実装をお見せしたい。

アイデアはとても簡単で、tuple の要素へアクセスするための std::get に渡すテンプレートパラメータを、再帰呼び出しと部分特殊化によって満たす。ただし、注意したいのは、そのパラメータとなるインデックスが降順に適用されるところである。

template< typename T, size_t N = std::tuple_size<T>::value >
struct tuple_map
{
    static void apply( const T& t, const std::function<void( const typename std::tuple_element<0, T>::type&, const size_t )>& map_func )
    {
        map_func( std::get<N-1>( t ), N-1 );
        return tuple_map<T, N-1>::apply( t, map_func );
    }
};

template< typename T >
struct tuple_map<T, 0>
{
    static void apply( const T&, const std::function<void( const typename std::tuple_element<0, T>::type&, const size_t )>& ) {}
};

クラス名、関数名がもし適切ではないとしてもご容赦いただきたい。(ご助言、お待ちしております。)map_func 関数に対して、tuple 要素のインデックスを渡すことができるのも、この tuple_map の特徴である。

前提にあるように、tuple の要素はすべて同一の型であるので、map_func の型は決め打ちできる。(tuple の最初の要素の型を基準にする。)これを満たさない場合は、コンパイルエラーとなる。

使用例

前述のコレクションに対して使用する。

tuple_map<decltype(list)>::apply( list, []( const color& c, const size_t index ) { std::cout << index << static_cast<uint32_t>(c) << std::endl; } );

インデックスを降順に走査するので、b, g, r の順に値が出力される。

おわりに

筆者は皆さんと同様、要求に対してやや大げさな実装をしたような感覚を否めない。しかし、冗長なコードをなくし、スッキリさせることができるのは間違いない。tuple と template を使った1つの遊び、楽しんでいただけただろうか。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.