4

この質問で、TMP を使用した再帰的なデータ構造生成の手法を把握しようとしています。

質問

variadic template があるとしますtemplate<typename... Ts> struct my_sets { };

で、my_setsデータ メンバが に依存する新しい型を生成したいと考えていTsます。

たとえば、あるすべての要素/型に対してmy_sets1 つのデータ メンバーが必要です。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<>

これを達成する 1 つの方法は、サブクラス化と再帰を使用することだと思いますが、よくわかりません。

fwiw、無料の関数または単純な関数のオーバーロードを介してミューテーターを実装する方が簡単な場合は、それも問題ありません。

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 に答える