4

我试图通过这个问题掌握使用 TMP生成递归数据结构的技术。

问题

假设我有一个可变参数模板 template<typename... Ts> struct my_sets { };

my_sets我想生成一个新类型,其数据成员依赖于Ts.

例如,我想为其中的每个元素/类型my_sets拥有一个数据成员std::set<T>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<>

我认为实现这一点的一种方法可能是通过使用子类化和递归,但我不确定。

fwiw,如果通过自由函数或普通函数重载来实现 mutator 更直接,那也没关系:

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<>
4

2 回答 2

12

这里有什么问题std::tuple

#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");

对于外观,添加一个免费insert功能:

#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));
}
于 2013-03-14T23:42:16.690 回答
2

@Xeo 有一个优雅而简单的解决方案。但是,如果您想将其insert<>作为成员函数,则可以使用以下方法:

#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;
    }
};

这就是你将如何使用它

#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);
}

请注意,上述解决方案不允许在类型列表中多次出现同一类型(可能需要也可能不需要),尽管它可以很容易地扩展以允许它们:

#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...
    >;

这就是你将如何使用它:

#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);
}
于 2013-03-15T00:13:46.590 回答