tupleを作ってみた

対数オーダーで作りたくて書き始めたのですが、思ったより難しそうだったのでとりあえず基本の線形実装です。
ムーブコンストラクタ等実装してません…。

実装法は幾つかあると思いますが、これではpairの入れ子による構築、firstをvalue、secondをpairにしてリスト風に実装しています。 継承を用いて実装しているライブラリもあったような気がします。

葉を除いてfirstもsecondもpairにして葉で両方をvalueにする木構造チックな方針で構築すれば対数オーダー化出来る気がします。
今度挑戦してみます。

#include <type_traits>
#include "sprout/utility.hpp"
#include "toki/traits.hpp"

template<class... Types>
struct tuple
{
private:
    template<class T>
    struct single_data
    {
        constexpr single_data(T arg) : first(arg) {};
        const T first;
    };
    
    template<class T, class... Ts, typename std::enable_if<(sizeof...(Ts) >= 1)>::type*& = toki::enabler>
    constexpr auto create_tuple(T Arg, Ts... Args)
    {
        return sprout::make_pair(Arg, create_tuple(Args...));
    }
    
    template<class T>
    constexpr auto create_tuple(T Arg)
    {
        return single_data<T>(Arg);
    }
    
    template<class T, class... Ts, typename std::enable_if<(sizeof...(Ts) >= 1)>::type*& = toki::enabler>
    static auto data_type()
    {
        return toki::type_wrapper<sprout::pair<T, typename decltype(data_type<Ts...>())::type>>();
    }
    
    template<class T>
    static auto data_type() -> toki::type_wrapper<single_data<T>>;
    
public:
    constexpr tuple(Types... Args) : data(create_tuple(Args...)) {};
    const typename decltype(data_type<Types...>())::type data;
};

namespace detail {
    
    template<std::size_t N>
    struct get_impl
    {
        template<class T>
        constexpr auto operator ()(T arg)
        {
            return get_impl<N - 1>()(arg.second);
        }
    };
    
    template<>
    struct get_impl<0>
    {
        template<class T>
        constexpr auto operator ()(T arg)
        {
            return arg.first;
        }
    };
    
}

template<std::size_t N, class T>
constexpr auto get(T arg)
{
    return detail::get_impl<N>()(arg.data);
}

int main()
{
    constexpr tuple<int, double, char> a(1, 2.0, 'a');
    
    static_assert(get<0>(a) == 1, "");
    static_assert(get<1>(a) == 2.0, "");
    static_assert(get<2>(a) == 'a', "");
}
  

よくよく考えてみればoperator()()はstaticに出来ますね…。