Инициализируйте `std :: array` с аргументами пакета параметров - PullRequest
0 голосов
/ 31 января 2019

Есть структура HasArray с параметрами шаблона typename T и size_t N.

template<typename T, size_t N>
struct HasArray {
  // enable_if sizeof...(T) equals N
  template<typename ... Types, typename std::enable_if<sizeof...(Types) == N, int>::type = 0>
  explicit HasArray(Types ... s) : arr { s... } {}
 protected:
  std::array<T, N> arr;
};

Я хотел бы инициализировать массив элементов arr с помощью аргументов пакета параметров конструктора,

HasArray<uint32_t, 2> foo(7, 13);

Но это приводит к предупреждению c++11-narrowing в Clang.

error: non-constant-expression cannot be narrowed from type 'int' to 'std::__1::array<unsigned int, 2>::value_type' (aka 'unsigned int') in initializer list [-Wc++11-narrowing]

Я не вижу способа привести все s типа Types к типу T.Есть один?

Редактировать Спасибо за все ответы.В итоге я использовал static_cast<> для упакованных параметров и SFINAE, когда не конвертируемо:

template<typename T, size_t N>
struct HasArray {
  // Use `static_cast` to construct `arr` with `s`
  // Add `enable_if` all `Types` are `is_convertible`
  template<typename ... Types, 
    typename std::enable_if<sizeof...(Types) == N, int>::type = 0,
    typename std::enable_if<(std::is_convertible<Types, T>::value && ...), int>::type = 0>
  explicit HasArray(Types ... s) : arr { static_cast<T>(s)... } {}
 protected:
  std::array<T, N> arr;
};

Ответы [ 3 ]

0 голосов
/ 31 января 2019

Я не вижу способа обеспечить, чтобы все типы Типы были типа T. Есть ли один?

Я не понимаю, из вашего вопроса, если вы хотите, чтобы Types являются выводимыми типами шаблонов, и все они выводятся точно как T, или если вы хотите конструктор без шаблона, который получает точность N значений типа T.

Первый случай прост (если вы можете использовать свертывание шаблона C ++ 17; в противном случае немного сложнее), потому что вы можете использовать std::is_same

template <typename ... Types,
          typename std::enable_if<(sizeof...(Types) == N) 
              && (... && std::is_same<Types, T>::value), int>::type = 0>
explicit HasArray(Types ... s) : arr {{ s... }}
 { }

Для второго случая я предлагаю вариантРешение Jarod42, которое использует специализацию HasArray вместо промежуточного класса (редактирование: добавлено улучшение от самого Jarod42; спасибо!):

template<typename T, std::size_t N, typename = std::make_index_sequence<N>>
struct HasArray;

template<typename T, std::size_t N, std::size_t ... Is>
struct HasArray<T, N, std::index_sequence<Is...>>
 {
   static_assert( sizeof...(Is) == N , "wrong sequence size" );

   protected:
      std::array<T, N> arr;

   public:
      explicit HasArray(getType<T, Is> ... s) : arr {{ s... }}
       { }
 };

, где getType равно

template <typename T, std::size_t>
using getType = T;

В первом случае

HasArray<std::uint32_t, 2> foo(7, 13);

выдает ошибку компиляции, поскольку 7 и 13 выводятся как int, что не std::uin32_t.

Во втором случае этокомпиляция, потому что HasArray имеет конструктор

HasArray(std::uint32_t, std::uint32_t)

и int s конвертируютсяed std::uint32_t.

Ниже приведен пример полной компиляции C ++ 14 для второго случая

#include <array>
#include <cstdint>
#include <type_traits>

template <typename T, std::size_t>
using getType = T;

template<typename T, std::size_t N, typename = std::make_index_sequence<N>>
struct HasArray;

template<typename T, std::size_t N, std::size_t ... Is>
struct HasArray<T, N, std::index_sequence<Is...>>
 {
   static_assert( sizeof...(Is) == N , "wrong sequence size" );

   protected:
      std::array<T, N> arr;

   public:
      explicit HasArray(getType<T, Is> ... s) : arr {{ s... }}
       { }
 };

int main ()
 {
   HasArray<std::uint32_t, 2> foo(7, 13);
 }
0 голосов
/ 31 января 2019

Если вы хотите построить std::array из любого значения, конвертируемого в T, то самым простым решением было бы просто добавить static_cast<T>(...) в ваш конструктор.

template<typename T, size_t N>
struct HasArray {
  template<typename ... Types,
           typename std::enable_if<sizeof...(Types) == N, int>::type = 0>
  explicit HasArray(Types ... s) : arr {static_cast<T>(s)... } {}
protected:
  std::array<T, N> arr;
};

https://godbolt.org/z/TEoZOG

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

0 голосов
/ 31 января 2019

Вы можете использовать промежуточный класс:

template <std::size_t, typename T>
using always_t = T;

template <typename T, typename Seq>
struct HasArrayImpl;

template <typename T, std::size_t...Is>
struct HasArrayImpl<T, std::index_sequence<Is...>>
{
    explicit HasArrayImpl(always_t<Is, T> ... v) : arr { v... } {}
protected:
    std::array<T, sizeof...(Is)> arr;
};

template <typename T, std::size_t N>
using HasArray = HasArrayImpl<T, std::make_index_sequence<N>>;

Иначе, вы можете расширить свой SFINAE до конвертируемого типа и явно преобразовать значения

template<typename T, size_t N>
struct HasArray {
    // enable_if sizeof...(T) equals N
    template <typename ... Types,
             std::enable_if_t<(sizeof...(Types) == N)
                                   && (std::is_convertible<Types, T>::value && ...),
                              int>::type = 0>
    explicit HasArray(Types ... s) : arr{ static_cast<T>(s)... } {}
 protected:
    std::array<T, N> arr;
};
...