конструктор с шаблонными аргументами variadi c одного типа не компилируется - PullRequest
2 голосов
/ 22 января 2020

Я пытаюсь создать класс, используя шаблонный шаблон variadi c. Все аргументы шаблона имеют одинаковый тип.

Я использую компилятор Visual C ++ с C ++ 17, но код не компилируется; этот код фактически приводит к сбою компилятора.

Возможно ли это в C ++ 17?

 struct Alfa {
     template<int... T>
     Alfa(const T&... all_args) {
         std::tuple<T...> data(all_args...);       
     }    
 };

 Alfa alfa(1,2,3,4);

Ответы [ 2 ]

2 голосов
/ 22 января 2020

Все аргументы шаблона имеют одинаковый тип. [...] возможно ли это в C ++ 17?

Да, это возможно.

Но не так просто и с некоторыми недостатками.

Вы можете написать конструктор, принимающий аргументы другого типа; это просто

 template <typename ... Ts>
 Alfa (const Ts & ... as) 
  { std::tuple<Ts...> data{as...}; } 

, но это позволяет различать типы Ts....

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

 template <typename T, typename ... Ts,
           std::enable_if_t<(std::is_same_v<T, Ts> && ...), bool> = true>
 Alfa (T const & a0, Ts const & ... as)
  {
     std::tuple<T, Ts...> data0{a0, as...};       
     std::array           data1{a0, as...};
  }  

, поэтому ваш конструктор включен, только если все типы Ts..., следующие за первым T, в точности совпадают с T

Недостаток: работает с

   Alfa alfa{1, 2, 3, 4};

но выдает ошибку с

   Alfa alfa{1l, 2l, 3, 4l};  <-- 3 isn't long

, потому что 3 конвертируется в long (1l - long), но не long.

Так что вы можно проверить, можно ли преобразовать следующие Ts... в T; вместо этого они равны

 template <typename T, typename ... Ts,
           std::enable_if_t<(std::is_convertible_v<Ts, T> && ...), bool> = true>
 Alfa (T const & a0, Ts const & ... as)
  {
     std::tuple<T, Ts...>             data0{a0, as...};       
     std::array<T, sizeof...(Ts)+1u>  data1{a0, as...};
  }   

, но таким образом вы придаете T большее значение для других типов (работает, если все * 1042) * могут быть преобразованы в T, но не в случае, если T может быть преобразовано в один из Ts...), поэтому я полагаю, что лучшим решением является проверка, существует ли общий тип

 template <typename ... Ts,
           typename CT = std::common_type_t<Ts...>>
 Alfa (Ts const & ... as)
  {
     std::tuple<Ts...>              data0{as...};       
     std::array<CT, sizeof...(Ts)>  data1{as...};
  }  
0 голосов
/ 22 января 2020

Вы должны решить, хотите ли вы использовать нетипизированные аргументы шаблона или нет. Ваш код сначала говорит «T - целочисленные значения», но затем пытается использовать T как тип в сигнатуре функции и tuple список аргументов шаблона.

Допустимые параметры:

 struct Alfa {
     template<class ... Ts>
     Alfa(const Ts&... all_args) {
         std::tuple<Ts...> data(all_args...);       
     }    
 };

 Alfa alfa(1,2,3,4);

Это не кодирует ограничение "все Ts должны быть одинаковыми". Там нет прямого способа сделать это. Вы можете использовать static_assert или SFINAE (см. Другой ответ).

 template<int... Is>
 struct Alfa {
     Alfa() {
         std::tuple data(Is...); // Using class template argument deduction.
     }    
 };

 Alfa<1,2,3,4> alfa{};

Это шаблон класса, а не конструктора, что, скорее всего, не то, что вам нужно. Он показывает синтаксис для принятия целочисленных значений времени компиляции (то есть нетипизированный пакет параметров шаблона). Но вы не можете использовать удержание для нетипичных параметров шаблона, поэтому они должны быть указаны явно. Но это невозможно для конструктора!

Компромисс может быть:

 struct Alfa {
     template<class ... Ts>
     Alfa(const Ts&... all_args) {
         std::tuple<Ts...> data(all_args...);       
     }    
 };

 template<int ... Is>
 auto makeAlfa() { return Alfa(Is...); }

 Alfa alfa = makeAlfa<1,2,3,4>();
...