Развернуть пакет шаблонов Variadic по парам

Я создаю общую коллекцию Nodes. Каждый Node имеет тип Start и End. И тип End одного должен совпадать с типом Start следующего.

Если бы я перечислил все типы в коллекции, конструктор выглядел бы так (для четырех типов):

template <typename Start, typename End>
class Node {
};

template <typename A, typename B, typename C, typename D>
class Collection
{
public:
    Collection(Node<A, B> n1, Node<B, C> n2, Node<C, D> n3) { }
};

Но когда я пытаюсь написать конструктор как вариативный шаблон для поддержки любого количества типов, я теряюсь.

Было бы здорово показать вашу попытку написать конструктор с вариативным шаблоном. Возможно, мы сможем не только помочь, но и проанализировать ваш мыслительный процесс и указать, где именно логика, которую вы придумали, ошибочна.

Fureeish 29.05.2019 21:18

Можете ли вы использовать С++ 17?

AndyG 29.05.2019 21:25
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
154
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

С некоторой косвенностью вы можете сделать:

template <typename Start, typename End>
class Node {
    // ...
};

// Implementation using the Nodes
// You might add typedef in Node to retrieve Start/End if needed (or create traits)
template <typename ... Nodes>
struct CollectionImpl
{
    CollectionImpl(Nodes ... ns) : nodes(ns...){}

    std::tuple<Nodes...> nodes; // You probably want something like that
};

// Helper class to build the type
template <typename Seq, typename Tup> struct CollectionMaker;

template <std::size_t ... Is, typename Tuple>
struct CollectionMaker<std::index_sequence<Is...>, Tuple>
{
    using type = CollectionImpl<Node<std::tuple_element_t<Is, Tuple>,
                                     std::tuple_element_t<Is + 1, Tuple>>...>;
};

// Wanted interface.
template <typename ... Ts>
using Collection = typename CollectionMaker<std::make_index_sequence<sizeof...(Ts) - 1>,
                                            std::tuple<Ts...>>::type;

Демо

Ответ принят как подходящий

Я предлагаю немного другое решение.

Учитывая тривиальную структуру tag для переноса универсального типа (чтобы избежать проблем с типами, которые по умолчанию не конструируются в std::tupless)

template <typename>
struct tag
 { };

и вспомогательная структура, которая определяет 2 типа на основе std::tuple

template <typename...>
struct getTpls;

template <std::size_t ... Is, typename ... Ts>
struct getTpls<std::index_sequence<Is...>, Ts...>
 {
   using tpl0 = std::tuple<tag<Ts>...>;
   using ftpl = std::tuple<std::tuple_element_t<Is,    tpl0>...>;
   using stpl = std::tuple<std::tuple_element_t<1u+Is, tpl0>...>;
 };

вы можете написать Collection следующим образом

template <typename ... Ts>
struct Collection
 {
   static_assert( sizeof...(Ts) > 1u, "more types, please");

   using getT = getTpls<std::make_index_sequence<sizeof...(Ts)-1u>, Ts...>;

   using ftpl = typename getT::ftpl;
   using stpl = typename getT::stpl;

   template <typename ... FTs, typename ... STs,
             std::enable_if_t<
                 std::is_same_v<ftpl, std::tuple<tag<FTs>...>>
              && std::is_same_v<stpl, std::tuple<tag<STs>...>>, int> = 0>
   Collection (Node<FTs, STs> ...)
    { }
 };

Ниже приведен полный пример компиляции

#include <tuple>
#include <type_traits>

template <typename Start, typename End>
class Node
 { };

struct A {};
struct B {};
struct C {};

template <typename>
struct tag
 { };

template <typename...>
struct getTpls;

template <std::size_t ... Is, typename ... Ts>
struct getTpls<std::index_sequence<Is...>, Ts...>
 {
   using tpl0 = std::tuple<tag<Ts>...>;
   using ftpl = std::tuple<std::tuple_element_t<Is,    tpl0>...>;
   using stpl = std::tuple<std::tuple_element_t<1u+Is, tpl0>...>;
 };

template <typename ... Ts>
struct Collection
 {
   static_assert( sizeof...(Ts) > 1u, "more types, please");

   using getT = getTpls<std::make_index_sequence<sizeof...(Ts)-1u>, Ts...>;

   using ftpl = typename getT::ftpl;
   using stpl = typename getT::stpl;

   template <typename ... FTs, typename ... STs,
             std::enable_if_t<
                 std::is_same_v<ftpl, std::tuple<tag<FTs>...>>
              && std::is_same_v<stpl, std::tuple<tag<STs>...>>, int> = 0>
   Collection (Node<FTs, STs> ...)
    { }
 };

int main ()
 {
   Collection<A, B, C>  c0{Node<A, B>{}, Node<B, C>{}};    // compile
   // Collection<A, B, B>  c1{Node<A, B>{}, Node<B, C>{}}; // error!
 }

Спасибо, мне нравится, как ваш ответ оставляет конструктор больше всего похожим на предполагаемые данные, которые его заполняют. Я не думаю, что шаблоны должны когда-либо запутывать цель функции.

Rhaokiel 30.05.2019 15:04

Часть, которой мне не хватало в моем коде, заключалась в том, как вывести типы FT и ST. Я не знал, что для этого можно использовать enable_if.

Rhaokiel 30.05.2019 15:05

На основе ответа max66:

Это убирает ненужную структуру tag и упрощает index_sequence прямую рекурсию (аналогично определению кортежа).

template <typename Owner, typename Value>
class Node {
};


struct A {};
struct B {};
struct C {};
struct D {};


template <typename First, typename...Rest>
std::tuple<First, Rest...> tuple_push_front(std::tuple<Rest...>);

template <typename T1, typename T2, typename...T>
struct NodeCollector {
private:
    using nodeRest = NodeCollector<T2, T...>;
public:
    using tplOwners = decltype(tuple_push_front<T1>(std::declval<typename nodeRest::tplOwners>()));
    using tplValues = decltype(tuple_push_front<T2>(std::declval<typename nodeRest::tplValues>()));
};
template <typename T1, typename T2>
struct NodeCollector<T1, T2> {
public:
    using tplOwners = std::tuple<T1>;
    using tplValues = std::tuple<T2>;
};


template <typename...Ts>
class Collection
{
    static_assert( sizeof...(Ts) > 1u, "Collection requires at least two types.");
private:
    using nodeCollector = NodeCollector<Ts...>;
public:
    template <typename...OTs, typename...VTs, typename=std::enable_if_t<
        (std::is_same_v<typename nodeCollector::tplOwners, std::tuple<OTs...>> &&
         std::is_same_v<typename nodeCollector::tplValues, std::tuple<VTs...>>)> >
    Collection(Node<OTs, VTs>...) { }
};


int main()
{
    Collection<A, B, C, D> c{Node<A, B>{}, Node<B, C>{}, Node<C, D>{}};
    std::cout << demangle(typeid(c).name()) << std::endl;
}

Другие вопросы по теме