C++ 如何在可变模板上生成递归数据结构?

C++ 如何在可变模板上生成递归数据结构?,c++,templates,c++11,variadic-templates,recursive-datastructures,C++,Templates,C++11,Variadic Templates,Recursive Datastructures,我试图掌握使用TMP生成递归数据结构的技术 问题 假设我有一个可变模板模板结构my_set{} 在my_set中,我想生成一个新类型,其数据成员依赖于Ts 例如,我希望my_set中的每个元素/类型都有一个std::set数据成员。 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

我试图掌握使用TMP生成递归数据结构的技术

问题

假设我有一个可变模板
模板结构my_set{}

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

例如,我希望
my_set
中的每个元素/类型都有一个
std::set
数据成员。

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<>
使用x\u t=my\u集;
x_t x;
x、 插入(5);//进入my_集合中的std::set成员
x、 插入('z');//进入my_集合中的std::set成员
x、 插入(“foo”);//进入my_集合中的std::set成员
我认为实现这一点的一种方法可能是通过使用子类化和递归,但我不确定

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<>
插入(x,5);//进入my_集合中的std::set成员
插入(x,'z');//进入my_集合中的std::set成员
插入(x,“foo”);//进入my_集合中的std::set成员

这里的
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");

@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;
    }
};
#包括
#包括
模板
结构我的集合:受保护的std::集合。。。
{
使用types=std::tuple;
模板
typename std::pair<
typename std::set::迭代器,
布尔>插入(T&T)
{
返回std::set::insert(
标准:正向(t)
);
}
// ...
//用于检索每个集合的函数。。。
模板
typename std::set&get()
{
归还*这个;
}
};
这就是你将如何使用它

#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);
}
#包括
int main()
{
我的妈妈;
s、 插入(42);
s、 插入(3.14);
s、 插入(“你好,世界!”);
s、 get().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...
    >;
#包括
#包括
名称空间详细信息
{
模板
结构索引
{
然后键入def索引;
};
模板
结构索引范围
{
使用type=typename索引\ u范围::type::next;
};
模板
结构索引范围
{
使用类型=指数;
};
模板
结构虚拟:T{};
模板
结构my_集{};
模板
结构my_集:受保护的虚拟对象。。。
{
使用types=std::tuple;
模板
typename std::pair<
typename std::set::迭代器,
布尔
>插入(T&T)
{
返回虚拟对象::
插入(标准:正向(t));
}
模板
dummy&get()
{
归还*这个;
}
};
}
模板
使用my_集=详细信息::my_集<
typename详细信息::索引\范围::类型,
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);
}
#包括
int main()
{
我的妈妈;
s、 插入(42);
s、 插入(3.14);
s、 插入(1729);
s、 插入(“你好,世界!”);
s、 get().insert(42);
}

+1 v.nice-不知道你可以断开。。。就像那样,但当我盯着它看了一会儿,语法实际上是非常有意义的@kfmfe04:Pack expansion只是说“这是一个模式,复制并替换包中的所有内容”。我看到了典型的
Ts…
扩展-是
std::set…
让我非常惊讶!我敢打赌,对于编译器开发人员来说,让这段代码正常解析和运行是非常重要的。但是现在我看了下面Andy的例子,我不太确定我是否理解他的用法——他是在从std::set中创建一大堆单独的子类吗?这很好,你可以做到。@kfmfe04:是的,这也是允许的。我基本上是从软件包获得的所有类型中继承
my_集expansion@kfmfe04C++11的设计考虑到了解析的易用性,我猜它的实现非常简单!我不认为从
std::set
继承有什么好处,特别是在保持
std::tuple
作为成员函数的情况下。如果你如此热衷于从某些东西继承,为什么不至少从
std::tuple
继承呢?:/这解决了多类型的问题,而没有所有的缺点(或者,你知道,你可以把它作为成员)。我真的不明白你为什么要把它作为基类。@Xeo:没必要生气;)我只是把这个问题(我发现这个问题已经得到了回答)作为一个展示一些技术/设计模式的机会,这些技术/设计模式可能对某些人来说是有趣或有用的(索引、从扩展类型列表继承、多参数包扩展等)。我承认你的解决方案更简单、更惯用,如果我在你回答这个问题之前就发现了这个问题,我会把它贴出来。
#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);
}