_Generic

C11から導入された機能。なんというか、色々と面白い事に使えそうな感じの機能。タプルなんかと多分相性が良い。曲芸的濫用についてはまた今度考える。
ラムダ式オーバーロードする最も簡単な方法かもしれない。 http://melpon.org/wandbox/permlink/14pwv0Akg5DhrtnA

#include <iostream>

int main() {
    auto f = [](auto arg) {
        std::cout << _Generic(arg, int: arg + 1, double: arg - 1.0, default: arg) << std::endl;
    };
    f(1);
    f(1.5);
}

複数の型の組み合わせで分岐したい場合、1つにまとめるとなんとかなる。
caseに当たる部分にはcomplete typeが要求され、tag<int, int>:と書いてもテンプレートのインスタンス化が行われずにコンパイルエラーになったが、delctype(tag<int, int>{}):とワークアラウンドを書いたところうまく動いた。
http://melpon.org/wandbox/permlink/WpLjU1hyNgcUERUX

#include <iostream>

template <typename...> struct tag {};
    
template <typename T1, typename T2>
auto f(T1 arg1, T2 arg2) {
    std::cout << _Generic(tag<T1, T2>{}, decltype(tag<int, int>{}): arg1 * arg2, decltype(tag<double, double>{}): arg1 * arg2)
              << std::endl;
}

int main() {
    f(3, 5);
    f(1.5, 2.0);
}

memo

N4072: Fixed Size Parameter Packs

O(1)

#include <type_traits>

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

template <std::size_t N, typename Tuple>
struct at;
template <std::size_t N, template <typename...> class Tuple, typename... Types>
struct at<N, Tuple<Types...>> {
    template <typename...[N] Args, typename Arg>
    static auto impl(Args..., Arg, ...) -> Arg;
    
    using type = typename decltype(impl(identity<Types>{}...))::type;
};

再帰深度を抑えた畳込み関数

FTMPのメタ関数で使われている畳込みの実装アプローチをソースコードと勉強会で発表された資料を参考に理解した(気になった)ので、constexprで再現してみました。パックの分割に当たる操作と、相互再帰を利用して畳み込みがうまく実現されています。foldlとfoldrのコードは殆ど同じですが一応両方載せておきます。

[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

#include <sprout/tuple.hpp>
#include <sprout/functional.hpp>
#include <iostream>

// foldl
template <typename Func, typename A, typename Bs>
constexpr A foldl(const Func& f, const A& a, const Bs& bs);

template <typename Func, typename A, typename B>
constexpr A foldl(const Func& f, const A& a, const sprout::tuple<B>& b);

template <typename Func, typename A>
constexpr A foldl(const Func& f, const A& a, const sprout::tuple<>&);

template <typename Func, typename A, typename Bs, sprout::index_t... LIndices,
          sprout::index_t... RIndices>
constexpr A foldl_impl(const Func& f, const A& a, const Bs& bs,
                       sprout::index_tuple<LIndices...>,
                       sprout::index_tuple<RIndices...>) {
  return foldl(
      f, foldl(f, a, sprout::forward_as_tuple(sprout::get<LIndices>(bs)...)),
      sprout::forward_as_tuple(sprout::get<RIndices>(bs)...));
}

template <typename Func, typename A, typename B>
constexpr A foldl(const Func& f, const A& a, const sprout::tuple<B>& b) {
  return f(a, sprout::get<0>(b));
}

template <typename Func, typename A>
constexpr A foldl(const Func&, const A& a, const sprout::tuple<>&) {
  return a;
}

template <typename Func, typename A, typename Bs>
constexpr A foldl(const Func& f, const A& a, const Bs& bs) {
  return foldl_impl(
      f, a, bs,
      sprout::index_range<0, sprout::tuple_size<Bs>::value / 2>::make(),
      sprout::index_range<sprout::tuple_size<Bs>::value / 2,
                          sprout::tuple_size<Bs>::value>::make());
}

// foldr
template <typename Func, typename A, typename Bs>
constexpr A foldr(const Func& f, const A& a, const Bs& bs);

template <typename Func, typename A, typename B>
constexpr A foldr(const Func& f, const A& a, const sprout::tuple<B>& b);

template <typename Func, typename A>
constexpr A foldr(const Func& f, const A& a, const sprout::tuple<>&);

template <typename Func, typename A, typename Bs, sprout::index_t... LIndices,
          sprout::index_t... RIndices>
constexpr A foldr_impl(const Func& f, const A& a, const Bs& bs,
                       sprout::index_tuple<LIndices...>,
                       sprout::index_tuple<RIndices...>) {
  return foldr(
      f, foldr(f, a, sprout::forward_as_tuple(sprout::get<RIndices>(bs)...)),
      sprout::forward_as_tuple(sprout::get<LIndices>(bs)...));
}

template <typename Func, typename A, typename B>
constexpr A foldr(const Func& f, const A& a, const sprout::tuple<B>& b) {
  return f(sprout::get<0>(b), a);
}

template <typename Func, typename A>
constexpr A foldr(const Func&, const A& a, const sprout::tuple<>&) {
  return a;
}

template <typename Func, typename A, typename Bs>
constexpr A foldr(const Func& f, const A& a, const Bs& bs) {
  return foldr_impl(
      f, a, bs,
      sprout::index_range<0, sprout::tuple_size<Bs>::value / 2>::make(),
      sprout::index_range<sprout::tuple_size<Bs>::value / 2,
                          sprout::tuple_size<Bs>::value>::make());
}


int main() {
  std::cout << foldl(sprout::plus<int>{}, 0, sprout::make_tuple(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
            << std::endl;
  std::cout << foldr(sprout::plus<int>{}, 0, sprout::make_tuple(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
            << std::endl;
}

N4072: Fixed Size Parameter Packs は期待出来る

N4072: Fixed Size Parameter Packsという提案が出ています。

参考記事:
本の虫: 2014-07 post Rapperswil mailingのレビュー: N4070-N4079

そこにこのような例があります。

template<unsigned int N, unsigned int M>
void f(int...[N], int...[M]) {}

f<2>(1,2,3); // M is deduced as 1. (N can't be deduced, but is given.)

これが可能ならば定数オーダーでパックのN番目の要素を取り出すのは以下の様に書けることが期待出来るはず。

template <unsigned int N, typename...[N] Types, typename T>
auto f(Types&&... args, T&& arg, ...) {
  return std::forward<T>(arg);
}

f<2>(1, 2, 3, 4 ,5); // 3

もはやパラメータパック分割イディオムに頼ることもなく、このようなことが出来るのですね。同様にしてtype_atのようなメタ関数も簡単に定数オーダーで実装出来るのではないでしょうか。

期待が高まります。

overloaded_function, fusedの関数ポインタ対応を済ませた

overloaded_function, fusedの関数ポインタ対応を済ませました。
これでoverloaded_function, fusedの当面の目標を一通り実装しました。
また別のネタを模索しようと思います。