Подтвердить что ты не робот

Есть ли причина для std:: array с нулевым размером в С++ 11?

Рассмотрим следующий фрагмент кода, который идеально подходит компилятору С++ 11:

#include <array>
#include <iostream>

auto main() -> int {
  std::array<double, 0> A;

  for(auto i : A) std::cout << i << std::endl;

  return 0;
}

В соответствии со стандартом § 23.3.2.8 [Массивы с нулевым размером]:

1 Массив должен поддерживать специальный случай N == 0.

2 В случае, когда N == 0, begin() == end() == уникальное значение. Возвращаемое значение    data() не задано.

3 Невозможно выполнить вызов вызова front() или back() для массива нулевого размера.

4 Функция-член swap() должна иметь noexcept-спецификацию, которая эквивалентна    noexcept(true).

Как показано выше, нулевой размер std::array вполне допустим в С++ 11, в отличие от массивов нулевого размера (например, int A[0];), где они явно запрещены, но они разрешены некоторыми компиляторами (например, GCC) в стоимости undefined поведения.

Учитывая это "противоречие", у меня есть следующие вопросы:

  • Почему комитет С++ решил разрешить нулевой размер std::array s?

  • Существуют ли какие-либо ценные виды использования?

4b9b3361

Ответ 1

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

template<typename T, size_t N>
std::array<T, N> choose(const std::vector<T> &v) {
   ...
}

Ничего не получается, если это вызывает поведение undefined или ошибки компилятора, если N почему-то оказывается нулевым.

Для необработанных массивов причиной ограничения является то, что вам не нужны типы с sizeof T == 0, что приводит к странным эффектам в сочетании с арифметикой указателя. Массив с нулевыми элементами будет иметь нулевой размер, если вы не добавите ему никаких специальных правил.

Но std::array<> - это класс, а классы всегда имеют размер > 0. Таким образом, вы не сталкиваетесь с этими проблемами с std::array<>, и согласованный интерфейс без произвольного ограничения параметра шаблона является предпочтительным.

Ответ 2

Одно использование, о котором я могу думать, - это возвращение массивов нулевой длины и возможность функциональности, которую нужно проверить.

Например, см. документацию по функции std::array empty(). Он имеет следующее возвращаемое значение:

true if the array size is 0, false otherwise.

http://www.cplusplus.com/reference/array/array/empty/

Я думаю, что возможность возврата и проверки массивов длиной 0 соответствует стандарту для других реализаций типов stl, например. Векторы и карты поэтому полезны.

Ответ 3

Как и в других классах контейнеров, полезно иметь объект, представляющий массив вещей, и иметь возможность, чтобы этот массив был или стал пустым. Если это невозможно, тогда нужно будет создать другой объект или управляющий класс для представления этого состояния законным способом. Наличие этой способности, уже содержащейся во всех классах контейнеров, очень полезно. При использовании этого, тогда просто нужно привыкнуть относиться к массиву как к контейнеру, который может быть пустым, и проверять размер или индекс перед тем, как обратиться к его члену в тех случаях, когда он может не указывать на что-либо.

Ответ 4

На самом деле существует довольно много случаев, когда вы хотите это сделать. Он присутствует и на многих других языках. Например, Java фактически имеет Collections.emptyList(), который возвращает список, который не только равен нулю, но не может быть расширен или изменен или изменен.

Пример использования может быть, если у вас есть класс, представляющий собой шину, и список пассажиров внутри этого класса. Список может быть ленивым инициализированным, созданным только при посадке пассажиров. Если кто-то звонит getPassengers(), хотя затем пустой список может быть возвращен, а не создавать новый список каждый раз, чтобы сообщить пустое сообщение.

Возвращаемое значение null также будет работать на внутреннюю эффективность класса, но затем сделает жизнь намного более сложной для всех, кто использует класс, поскольку всякий раз, когда вы вызываете getPassengers(), вам нужно будет нуль проверить результат. Вместо этого, если вы получите пустой список, тогда, пока ваш код не сделает предположений, что список не пуст, вам не нужен какой-либо специальный код для обработки его нулевого значения.