简体   繁体   English

STD::Tuple 实现 C++

[英]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.参考下面的代码,我发现很难掌握元模板编程中递归和平面 STD::tuple 实现之间的主要区别。 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.我希望下面的代码能够帮助那些想知道 STD::tuple 在元模板编程中的实现的人。

#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.递归实现符合 C++11,因此当后续标准不可用时应该首选它,因为它是唯一的选择。 The shown flat implementation relies on C++17 by using if constexpr and C++14 by using std::index_sequence .所示的平面实现依赖于 C++17 通过使用if constexpr和 C++14 通过使用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.因此,当有足够的标准级别可用时,我更喜欢平面实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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