4

Я пытаюсь понять технику создание рекурсивной структуры данных с использованием TMP с этим вопросом.Как создать рекурсивные структуры данных для вариативных шаблонов?

Вопрос

Пусть у меня есть VARIADIC шаблон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, если оно более прямолинейно реализовать мутатор через свободную функцию или обычной перегрузки функций, это нормально, тоже:

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

ответ

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)); 
} 
+0

+1 v.nice - не знал, что вы можете * разорвать * на ... вот так, но когда я смотрел на него какое-то время, синтаксис на самом деле имеет смысл! – kfmfe04

+0

@ kfmfe04: Расширение пакета просто говорит «вот этот шаблон, копия и замена для всего, что есть в пакете». – Xeo

+0

Я видел типичное расширение «Ts ...» - это был «std :: set ...», который действительно меня удивил! Я бы поспорил, что разработчикам компилятора было нетривиально, чтобы этот фрагмент кода работал на синтаксическом разборе и работал правильно. Но теперь, когда я смотрю на пример Энди ниже, я не совсем уверен, что понимаю его использование - он делает целую кучу отдельных подклассов из std :: set <>? Это здорово, что вы можете это сделать. – kfmfe04

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

И это, как вы woul d использовать его:

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

Я не думаю, что унаследовать от 'std :: set' does * any * хорошо здесь, особенно для того, чтобы просто хранить' std :: tuple' из них в качестве функций-членов. – Xeo

+0

@Xeo: Благодарим за указание. Я немного изменил вещи –

+0

Если вы так наследованы от чего-то, почему бы не из 'std :: tuple' atleast? :/Это решает проблему с несколькими типами без всякой трещины (или, знаете, вы можете просто иметь ее в качестве членов). Я действительно очень не понимаю, почему вы хотите, чтобы это были базовые классы. – Xeo

Смежные вопросы