I am trying to grasp the technique of recursive data-structure generation using TMP with this question.

Question

Suppose I have a variadic template template<typename... Ts> struct my_sets { };.

In my_sets I would like to generate a new type whose data members depends on Ts.

For instance, I want my_sets to have one std::set<T> data member for every element/type that is in Ts...

using x_t = my_sets<int,char,std::string>;
x_t x;

x.insert<0>(   5   );  // into a std::set<int> member in my_sets<>
x.insert<1>(  'z'  );  // into a std::set<char> member in my_sets<>
x.insert<2>( "foo" );  // into a std::set<std::string> member in my_sets<>

I think one way of accomplishing this may be through the use of subclassing and recursion, but I'm not sure.

fwiw, if it is more straight-forward to implement the mutator through a free function or plain function overloading, that's ok, too:

insert<0>( x,   5   );  // into a std::set<int> member in my_sets<>
insert<1>( x,  'z'  );  // into a std::set<char> member in my_sets<>
insert<2>( x, "foo" );  // into a std::set<std::string> member in my_sets<>
有帮助吗?

解决方案

What's wrong with std::tuple here?

#include <tuple>
#include <set>

template<class... Ts>
using my_sets = std::tuple<std::set<Ts>...>;

// ...

auto x = my_sets<int, char, std::string>;

std::get<0>(x).insert(5);
std::get<1>(x).insert('z');
std::get<2>(x).insert("foo");

For looks, add a free insert function:

#include <utility>

template<std::size_t I, class SetTuple, class Arg>
auto insert(SetTuple& st, Arg&& arg)
  -> decltype(std::get<I>(st).insert(std::forward<Arg>(arg)))
{
  return std::get<I>(st).insert(std::forward<Arg>(arg));
}

其他提示

@Xeo has an elegant and simple solution. If you want to have insert<> as a member function, however, you can use the following approach:

#include <set>
#include <tuple>

template<typename... Ts>
struct my_sets : protected std::set<Ts>...
{
    using types = std::tuple<Ts...>;

    template<int I, typename T>
    typename std::pair<
        typename std::set<typename std::tuple_element<I, types>::type>::iterator,
        bool> insert(T&& t)
    {
        return std::set<typename std::tuple_element<I, types>::type>::insert(
               std::forward<T>(t)
               );
    }

    // ...

    // Function for retrieving each set...
    template<int I>
    typename std::set<typename std::tuple_element<I, types>::type>& get()
    {
        return *this;
    }
};

And this is how you would use it

#include <string>

int main()
{
    my_sets<int, double, std::string> s;
    s.insert<0>(42);
    s.insert<1>(3.14);
    s.insert<2>("Hello World!");

    s.get<0>().insert(42);
}

Notice, that the above solution doesn't allow for multiple occurrences of the same type in the type list (which may or may not be desired), although it can be quite easily extended to allow them:

#include <set>
#include <tuple>

namespace detail
{
    template<int... Is>
    struct indices
    {
        typedef indices<Is..., sizeof...(Is)> next;
    };

    template<int I>
    struct index_range
    {
        using type = typename index_range<I - 1>::type::next;
    };

    template<>
    struct index_range<0>
    {
        using type = indices<>;
    };

    template<int I, typename T>
    struct dummy : T { };

    template<typename, typename... Ts>
    struct my_sets { };

    template<int... Is, typename... Ts>
    struct my_sets<indices<Is...>, Ts...> : protected dummy<Is, std::set<Ts>>...
    {
        using types = std::tuple<Ts...>;

        template<int I, typename T>
        typename std::pair<
            typename std::set<typename std::tuple_element<I, types>::type>::iterator,
            bool
            > insert(T&& t)
        {
            return dummy<I, std::set<typename std::tuple_element<I, types>::type>>::
                insert(std::forward<T>(t));
        }

        template<int I>
        dummy<I, std::set<typename std::tuple_element<I, types>::type>>& get()
        {
            return *this;
        }
    };
}

template<typename... Ts>
using my_sets = detail::my_sets<
    typename detail::index_range<sizeof...(Ts)>::type,
    Ts...
    >;

And this is how you would use it:

#include <string>

int main()
{
    my_sets<int, double, int, std::string> s;

    s.insert<0>(42);
    s.insert<1>(3.14);
    s.insert<2>(1729);
    s.insert<3>("Hello World!");

    s.get<0>().insert(42);
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top