Распределите нетипичный пакет параметров по различным пакетам параметров шаблона - PullRequest
3 голосов
/ 24 октября 2019

Существует ли какой-либо синтаксис, с помощью которого я могу распределить пакет параметров нетипичного типа по параметрам пакета параметров шаблонов, ожидая пакеты нестандартного типа (разных размеров)? Поскольку это довольно запутанно, я считаю, что пример может помочь прояснить, что я имею в виду: https://godbolt.org/z/FaEGTV

template <typename T, int... I> struct Vec { };

struct A
{
    template<template<typename, int...> typename...  Container,
        typename... Ts, int... Is>
    A(Container<Ts,Is...>... );
};  

A a(Vec<int, 0>{}, Vec<double, 0>{});       // ok
A b(Vec<int, 0, 1>{}, Vec<double, 0, 1>{}); // ok
A c(Vec<int, 0>{}, Vec<double, 0, 1>{});    // error

Я хочу, чтобы строка, помеченная // error, работала с синтаксисом, аналогичным тому, который у меня есть. Понятно, что все будет хорошо, если я напишу специальный конструктор для этого случая. Тем не менее, я хочу, чтобы это работало для любого количества контейнеров, без необходимости явного объяснения для всех возможных случаев. Например, если у меня есть 2 контейнера a,b с индексами {0,1,2} и {0,1,2,3}, расширение должно выглядеть следующим образом: A(a[0],a[1],a[2], b[0],b[1],b[2],b[3]).

Я знаю, что могу сделать это рекурсивно, распаковав один контейнерза один раз и рекурсивно делегируя конструкторам, ожидающим в начале последовательность только плоских элементов. Мой вопрос заключается в том, возможно ли это более изящным, эффективным и менее многословным способом.

Ответы [ 3 ]

1 голос
/ 24 октября 2019

Например, если у меня есть 2 контейнера a,b, с индексами {0,1,2} и {0,1,2,3}, расширение должно выглядеть как A(a[0],a[1],a[2], b[0],b[1],b[2],b[3]).

Я знаю, что могу сделатьэто рекурсивно, распаковка одного контейнера за раз, и рекурсивное делегирование конструкторам, ожидающим последовательность только плоских элементов в начале. Мой вопрос заключается в том, возможно ли это более изящным, эффективным и менее многословным способом.

Принимаете ли вы решение, при котором расширение даст вам std::tuple с a[0],a[1],a[2], b[0],b[1],b[2],b[3]?

В этом случае вы можете следовать предложению Игоря, распаковать значения в контейнерах и упаковать их в кортежи и использовать std::tuple_cat() для объединения кортежей.

Я имею в виду ... учитывая контейнер /Преобразователь кортежей выполняется следующим образом:

template <template<typename, std::size_t...> typename C,
          typename T, std::size_t... Is>
auto getTpl (C<T, Is...> const & v)
 { return std::make_tuple(v.data[Is]...); } 

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

   template <typename ... Ts>
   A (Ts const & ... ts) : A{ std::tuple_cat( getTpl(ts)... ) }
    { } 

, а конечный конструктор будет

   template <typename ... Ts>
   A (std::tuple<Ts...> const & tpl)
    { /* do something with values inside tpl */ }

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

#include <iostream>
#include <string>
#include <tuple>

template <typename T, std::size_t ... Is>
struct Vec
 {
   T data [sizeof...(Is)] = { Is... };

   T const & operator[] (std::size_t i) const
    { return data[i]; }

   T & operator[] (std::size_t i)
    { return data[i]; }
 };

template <template<typename, std::size_t...> typename C,
          typename T, std::size_t... Is>
auto getTpl (C<T, Is...> const & v)
 { return std::make_tuple(v.data[Is]...); }

struct A
 {
   template <typename ... Ts>
   A (std::tuple<Ts...> const & tpl)
    { /* do something with values inside tpl */ }

   template <typename ... Ts>
   A (Ts const & ... ts) : A{ std::tuple_cat( getTpl(ts)... ) }
    { } 
 };  

int main ()
 {
   A a(Vec<int, 0>{}, Vec<double, 0>{});       // ok
   A b(Vec<int, 0, 1>{}, Vec<double, 0, 1>{}); // ok
   A c(Vec<int, 0>{}, Vec<double, 0, 1>{});    // ok, now
 }
1 голос
/ 24 октября 2019

Я бы просто сделал:

template <typename T>
struct is_container_type : std::false_type{};

template<template<typename, size_t...> typename  C, typename T, size_t... Is>
struct is_container_type<C<T, Is...>> : std::true_type
{
    // Potential `using` to retrieve C, T, Is...
};

struct A
{
    template<typename...  Cs, REQUIRES(is_container_type<Cs>::value && ...)>
    A(Cs... cs)
    {
        ((void)(std::cout << cs << "\n"), ...);
    }
}; 

Демо

С multi_apply (std::apply версия для нескольких кортежей) (вы можете найти там ), вы можете сделать:

struct A
{

    template<typename...  Ts, REQUIRES(!is_container_type<Ts>::value || ...)>
    A(Ts... ts)
    {
        ((std::cout << ts << " "), ...);
        std::cout << std::endl;
    }


    template<typename...  Cs, REQUIRES(is_container_type<Cs>::value && ...)>
    A(Cs... cs) :
        A(multiple_apply([](auto...args){ return A(args...); },
                         cs...) // if Cs is tuple-like
                         // as_tuple(cs)...) // convert Cs to tuple<int, /*..*/, int>
          )
    {
    }

}; 

Демо

0 голосов
/ 24 октября 2019

Мне удалось создать несколько неэффективное решение, которое строит больший вектор в первом аргументе при каждом рекурсивном шаге, а затем имеет конструктор, который принимает один вектор и расширяет его. Понятно, что это не идеально, так как N векторов создаются с общим количеством элементов N^2/2 в худшем случае. Вот пример реализации, иллюстрирующий то, что я сделал: https://coliru.stacked -crooked.com / a / 1f41f3793846cdb1

Я пробовал другую версию, где новые объекты не создаются, однако компилятор не сделалпо какой-то причине не удается вывести правильный конструктор (я предполагал, что это связано с несколькими расширениями) - эта версия определена в GROW_VECTOR 0 в приведенном выше примере.

...