`static_assert` в` std :: initializer_list <T>:: size` в функции - PullRequest
2 голосов
/ 09 марта 2020

В моих модульных тестах я хочу быстрый и (чистый sh) грязный способ присвоения значений массиву stati c -size C из initializer_list. Я не полный зверь, поэтому я хочу static_assert, чтобы размеры были одинаковыми. Я написал вспомогательную функцию set_array для этого:

template <typename T, std::size_t N>
constexpr void set_array(T (&x)[N], std::initializer_list<T>&& list) {
  assert(list.size() == N); // why can't I static_assert in C++17?
  for (std::size_t i = 0; i < N; ++i)
    x[i] = list.begin()[i];
}

с намерением использовать ее как set_array(foo, {1, 2, 3, 4}); с объявленным foo как int foo[4].

Я использую C ++ 17, поэтому std std::initializer_list<T>::size является constexpr, но как только я пропущу список через вызов функции, я теряю привилегию рассматривать его как constexpr, поскольку не могу ограничить параметры функции как constexpr.

Такое ощущение, что должно быть простое решение, которого я не вижу. Конечно, я мог бы представить некоторые извращенные игры метапрограммирования, в которые я мог бы играть, чтобы закодировать размер в типе, но это простой маленький помощник, который должен делать вещи чистыми и читаемыми, и я не хочу go гадить.

Вопрос: Есть ли простое решение, или я должен просто жить с утверждением времени выполнения? (Да, я знаю, что если мне дадут простое решение, я буду чувствовать себя глупо из-за того, что не видел его сам.) Думаешь, я поступаю неправильно? Это нормально, я открыт для предложений и ценю критику.

Подробности: Для полноты излагаются ошибки компилятора и информация о версии. В Clang версии 8.0.0-3 (которая поставляется с Ubuntu clang-8) я получаю:

error: static_assert expression is not an
      integral constant expression
  static_assert(list.size() == N);
                ^~~~~~~~~~~~~~~~

И с G CC 8.3.0 я получаю похожую ошибку, дополнительно сообщая мне, что list не является константным выражением.

Ответы [ 2 ]

2 голосов
/ 09 марта 2020

Причина, по которой это не удается, даже если size равен constexpr, заключается в том, что list - это , а не - переменная constexpr, поэтому любые вызовы функций-членов также не будут constexpr.

Однако еще не все потеряно. Что вы можете сделать, это использовать std::array вместо std::initializer_list, что позволяет вам даже избавиться от static_assert, например:

template <typename T, std::size_t N>
constexpr void set_array(T (&x)[N], std::array<T, N>&& list) {
  for (std::size_t i = 0; i < N; ++i)
    x[i] = list[i];
}                                                          

int main()                                                                       
{                                                                                
    int arr[4];
    set_array(arr, {1,2,3,4});
    std::cout << arr[3];
}

Если вы пытались использовать

set_array(arr, {1,2,3,4,5});

тогда вы получите ошибку компилятора, такую ​​как

main.cpp:12:16: note: candidate function [with T = int, N = 4] not viable: cannot convert initializer list argument to 'std::array<int, 4UL>'
constexpr void set_array(T (&x)[N], std::array<T, N>&& list) {
2 голосов
/ 09 марта 2020

Аргументы функций не являются константами.

Это допустимо в C ++:

void foo( bool which ) {
  std::initializer_list<int> a = {1,2,3};
  std::initializer_list<int> b = {1,2,3,4};
  int x[4];
  std::initializer_list<int> c = which?a:b;    
  set_array(x, c);
}

попробуйте это:

template<class T>
struct block_deduction_t{using type=T;};
template<class T>
using block_deduction = typename block_deduction_t<T>::type;

template <typename T, std::size_t N>
constexpr void set_array(T (&x)[N], block_deduction<T const(&)[N]> list) {
   for (std::size_t i = 0; i < N; ++i)
      x[i] = list[i];
}

теперь это позволяет вам опустить конечные элементы, но они будут инициализированы нулями.

Живой пример .

Опять же, это решение:

template <typename T, std::size_t N>
constexpr void set_array(T (&x)[N], T const(&list)[N]) {
   for (std::size_t i = 0; i < N; ++i)
      x[i] = list[i];
}

фактически блокирует невысокие правые стороны. Так может быть и лучше.

Синтаксис точно соответствует вашему.

Если вы хотите получить красивое сообщение об ошибке, и для типов правой руки, которые должны быть преобразованы, чтобы соответствовать типам левой стороны:

template <typename T, std::size_t N, std::size_t M>
constexpr void set_array(T (&x)[N], block_deduction<T> const(&list)[M]) {
   static_assert(M==N, "wrong number of elements");
   for (std::size_t i = 0; i < N; ++i)
      x[i] = list[i];
}

есть много вариантов.

...