简体   繁体   中英

STD::Tuple implementations C++

With reference to the code below, I find it hard to grasp the main difference between the recursion and flat STD::tuple implementations in meta template programming. If it was possible, I hope anyone would be able to explain the core difference between the two, and why one should be preferred over the other? Also, if possible, explain how each one works for starters like me who has just been introduced to meta template programming. I hope the code below is able to help anyone out there who are wondering about the implementations of STD::tuple in meta template programming.

#include <tuple>
#include <iostream>
#include <utility>


namespace flop
{
    namespace recursion
    { 
        template <typename...>
        class leaf{};

        template <typename CArg,typename... CArgs>
        class leaf<CArg, CArgs...> :
            public leaf<CArgs...>
        {
        public:
            template <typename Targ,typename... Targs>
            leaf(Targ&& num, Targs&&... nums) : 
                leaf<CArgs...>(std::forward<Targs>(nums)...), 
                _value{std::forward<Targ>(num)}
            {
            }

            CArg _value;
        };

        template <typename... Ts>
        using tuple = leaf<Ts...>;

        template <size_t N,typename CArg,typename... CArgs>
        class simplify_pack
        {
        public:
            using type = typename simplify_pack<N - 1, CArgs...>::type;
        };

        template <
            typename CArg,
            typename... CArgs
        >
        class simplify_pack<0, CArg, CArgs...>
        {
        public:
            using type = leaf<CArg, CArgs...>;
        };

        template <
            size_t N,
            typename... Ts
        >
        using simplify_pack_t = typename simplify_pack<N, Ts...>::type;

        template <
            size_t N,
            typename... Ts
        >
        auto&  get(tuple<Ts...>& t)
        {
            using base = simplify_pack_t<N, Ts...>;

            return static_cast<base&>(t)._value;
        }
    }

///////////////////////////////////////////////////////////////////////////////

    namespace flat
    {
        template <size_t N,typename T>
        class leaf
        {
        public:
            template <typename Targ>
            explicit leaf(Targ&& num) :
                _value{std::forward<Targ>(num)}
            {
            }

            T _value;
        };

        template <typename Seq,typename... Ts>
        class tuple_implem{};

        template <size_t... Ns,typename... Ts>
        class tuple_implem<std::index_sequence<Ns...>,Ts...> 
            : public leaf<Ns, Ts>...
        {
        public:
            template <typename... Targs>
            tuple_implem(Targs&&... nums) :
                leaf<Ns, Ts>{std::forward<Targs>(nums)}...
            {
            }
        };

        template <typename... Ts>
        using tuple = tuple_implem<
            std::make_index_sequence< sizeof...(Ts) >,  
            Ts...                                       
        >;

        template <size_t N,typename CArg,typename... CArgs>
        auto N_type_search_f()
        {
            if constexpr (N == 0)
            {
                return CArg{};
            }
            else
            {
                return N_type_search_f<N - 1, CArgs...>();
            }
        }

        template <
            size_t N,
            typename... Ts
        >
        using N_type_search = decltype( N_type_search_f<N, Ts...>() );

        template <
            size_t N,
            typename... Ts
        >
        N_type_search<N, Ts...>& get(tuple<Ts...>& t)
        {
            using base = leaf<N, N_type_search<N, Ts...> >;

            return static_cast<base&>(t)._value;
        }
    }
}

///////////////////////////////////////////////////////////////////////////////

int main()
{

    std::cout << "std::tuple" << std::endl;
    {
        namespace myn_s = std;

        myn_s::tuple<int, int, double> t{10, 20, 30.0};
        auto print_tuple = [&]()
        {
            std::cout
                << "\tFirst:\t" << myn_s::get<0>(t) << "\n"
                << "\tSecond:\t" << myn_s::get<1>(t) << "\n"
                << "\tThird:\t" << myn_s::get<2>(t) << std::endl;
        };

        print_tuple();

        myn_s::get<0>(t) += 100;
        myn_s::get<1>(t) += 200;
        myn_s::get<2>(t) += 300;

        print_tuple();
    }

    std::cout << "flop::recursion::tuple" << std::endl;
    {
        namespace myn_s = flop::recursion;

        myn_s::tuple<int, int, double> t{10, 20, 30.0};
        auto print_tuple = [&]()
        {
            std::cout
                << "\tFirst:\t" << myn_s::get<0>(t) << "\n"
                << "\tSecond:\t" << myn_s::get<1>(t) << "\n"
                << "\tThird:\t" << myn_s::get<2>(t) << std::endl;
        };

        print_tuple();

        myn_s::get<0>(t) += 100;
        myn_s::get<1>(t) += 200;
        myn_s::get<2>(t) += 300;

        print_tuple();
    }

    std::cout << "flop::flat::tuple" << std::endl;
    {
        namespace myn_s = flop::flat;

        myn_s::tuple<int, int, double> t{10, 20, 30.0};
        auto print_tuple = [&]()
        {
            std::cout
                << "\tFirst:\t" << myn_s::get<0>(t) << "\n"
                << "\tSecond:\t" << myn_s::get<1>(t) << "\n"
                << "\tThird:\t" << myn_s::get<2>(t) << std::endl;
        };

        print_tuple();

        myn_s::get<0>(t) += 100;
        myn_s::get<1>(t) += 200;
        myn_s::get<2>(t) += 300;

        print_tuple();
    }
}

why one should be preferred over the other?

The recursive implementation conforms to C++11, so it should be preferred when later standard is not available because it is the only option. The shown flat implementation relies on C++17 by using if constexpr and C++14 by using std::index_sequence .

The flat implementation is simper in my opinion. Also, I suspect that it will be faster to compile, although I recommend measuring to verify that is correct. As such, I would prefer the flat implementation when sufficient standard level is available.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM