首页 > 解决方案 > STD::Tuple 实现 C++

问题描述

参考下面的代码,我发现很难掌握元模板编程中递归和平面 STD::tuple 实现之间的主要区别。如果可能的话,我希望任何人都能够解释两者之间的核心区别,为什么应该首选一个而不是另一个?另外,如果可能的话,解释一下每个人是如何为像我这样刚接触元模板编程的初学者工作的。我希望下面的代码能够帮助那些想知道 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();
    }
}

标签: c++

解决方案


为什么一个应该优先于另一个?

递归实现符合 C++11,因此当更高版本的标准不可用时应该首选它,因为它是唯一的选择。所示的平面实现通过 using 依赖于 C++17,通过 using 依赖于 C if constexpr++14 std::index_sequence

在我看来,平面实现更简单。另外,我怀疑编译起来会更快,尽管我建议测量以验证它是否正确。因此,当有足够的标准级别可用时,我更喜欢平面实现。


推荐阅读