読者です 読者をやめる 読者になる 読者になる

生配列を std::array に変換する

C++

constexpr で扱えて,実行時の効率もよいデータ構造+アルゴリズム - 危ないRiSKのブログ
コンパイル時に配列を結合する - iorateの日記

index_tuple を使って constexpr な生配列から std::array への変換の処理をする配列を書いてみました.

int main()
{
    // コンパイル時に変換する
    constexpr int ints[] = {1, 2, 3};
    constexpr auto arr3 = to_array(ints);

    // std::array の operator[] は非 constexpr
    static_assert( arr3._M_instance[0]==1 &&
                            arr3._M_instance[1]==2 &&
                            arr3._M_instance[2]==3, "" );

    // 実行時呼び出しもOK
    char chars[] = "foobar";
    for(auto const& c : to_array(chars)){
        std::cout << c << std::endl;
    }
}

実装はこんな感じ.

#include <cstddef>
#include <array>

template< std::size_t... Indices >
struct index_tuple{};

template < std::size_t Start,
                   std::size_t Last,
                   class Acc = index_tuple<>,
                   bool Finish = (Start>=Last) >
struct index_range{
    typedef Acc type;
};

template < std::size_t Start,
                   std::size_t Last,
                   std::size_t... Indices >
struct index_range< Start, Last, index_tuple<Indices...>, false >
         : index_range<Start+1, Last, index_tuple<Indices..., Start>>
{};

template <class T, std::size_t N, std::size_t... Indices>
constexpr std::array<T, N> to_array_impl( T const(& native_array)[N], index_tuple<Indices...>)
{
    return {{ native_array[Indices]... }};
}

template <class T, std::size_t N>
constexpr std::array<T, N> to_array( T const(& native_array)[N])
{
    return to_array_impl( native_array, typename index_range<0, N>::type() );
}

index_range でアクセスしたい添字の列 0,1, ... ,N-1 の可変長パラメータパックを作成し,index_tuple でそれを受け取ります.
これの良いところは冒頭に書いたエントリーでも述べられていますが,関数呼び出し回数が抑えられ,コンパイル時だけでなく実行時にも良いパフォーマンスで実行できるところです.constexpr の機能をうまく利用できています.