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

template parameter packのN番目の型を取り出すアプローチ

これはメタプロする事自体を目的にした遊びで,実用という意味で考えればO(N)で線形再帰するべき. 線形再帰ならば誰でも読めるし,数十個ぐらいなら最も速い.もし真面目にテンプレート引数を1000個も渡すケースに陥っているならばそれは多分そこに至る時点で相当設計が悪いはず.

下2つの実装方針のうちN番目の型を取り出すだけなら前者の方が有意に早いです(手元のclangだとN = 10000で3倍くらい速い)

melpon.org

前者はidentity<Ts>*...をvoid*..., T*, ...で受ける方針,後者は多重継承しておいてオーバーロード解決に持ち込む方針. どちらも再帰深度が対数オーダーになるのでNが大きくなってもスケールはする.

そもそも言語仕様のレベルでランダムアクセス出来るようになってないことがイケてないというのはある.

#include <utility>

template <typename T>
struct identity {
    using type = T;
};

template <std::size_t>
using void_p = void*;

template <typename Indices>
struct at_impl;
template <std::size_t... Indices>
struct at_impl<std::index_sequence<Indices...>> {
  template <std::size_t>
  using void_ptr = void*;

  template <typename T>
  static T eval(void_ptr<Indices>..., T*, ...);
};


// split parameter pack 
template <std::size_t N, typename... Ts>
using at = typename decltype(
  at_impl<std::make_index_sequence<N>>::eval(static_cast<identity<Ts>*>(nullptr)...))::type;

template <std::size_t I, typename T>
struct indexed_identity {
  using type = T;
};

template <typename Indices, typename... Ts>
struct at2_impl;
template <std::size_t... Indices, typename... Ts>
struct at2_impl<std::index_sequence<Indices...>, Ts...> : indexed_identity<Indices, Ts>... {
  template <std::size_t N, typename T>
  static identity<T> eval(indexed_identity<N, T>);
};


// overload resolution
template <std::size_t N, typename... Ts>
using at2 = typename decltype(at2_impl<std::make_index_sequence<sizeof...(Ts)>, Ts...>::template eval<N>(
  std::declval<at2_impl<std::make_index_sequence<sizeof...(Ts)>, Ts...>>()))::type;

int main() {
    static_assert(std::is_same<int, at<9, int, int, int, int, int, int, int, int, int, int>>::value, "");
    static_assert(std::is_same<int, at2<9, int, int, int, int, int, int, int, int, int, int>>::value, "");
}