Получение ссылки на необработанный массив из std:: array

Какой канонический способ получить ссылку на std::array базовый массив raw (C)?

Метод data() возвращает только необработанный указатель, что делает его непригодным, например. для передачи в функции, которые принимают ссылку на необработанный массив известного размера.

Кроме того, есть ли веская причина, почему data() возвращает необработанный указатель, а не ссылку на базовый необработанный массив, или это просто недосмотр?

Ответ 1

Какой канонический способ получить std:: array, лежащий в основе raw (C) массив?

Невозможно получить базовый массив C.

Кроме того, есть ли веская причина, почему data() возвращает необработанный указатель, а не ссылка на базовый необработанный массив, или это просто надзор?

В обратном порядке: для std::array нет достаточной причины для предоставления базового массива C. Как вы уже сказали, массив C будет полезен (по необработанному указателю) только с функциями, получающими ссылку на C-массивы.

Когда в последний раз у вас была функция:

void foo(int (&arr)[5])

Me? Никогда. Я никогда не видел функцию с параметром ссылки на массив C, за исключением получения размера массива (и отклонения указателей):

template <class T, std::size_t N>
auto safe_array_size(T (&)[N]) { return N; }

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

Для начала, из указателя области C с отдельным параметром размера был единственным способом передачи массивов вокруг, из-за распада массива на указатель и отсутствия ссылочного типа.

В С++ существуют альтернативы массивам C, например std::vector и std::array. Но даже если у вас есть (устаревший) массив C, у вас есть 2 ситуации:

  • если вы передаете его функции C, у вас нет опции ссылки, поэтому вы привязаны к указателю + размер
  • когда вы хотите передать его функции С++, идиоматический способ С++ - передать указатели begin + end.

Прежде всего, итераторы begin + end являются универсальными, он принимает любые типы контейнеров. Но не редкость видеть ссылку на std::vector, когда вы хотите избежать шаблонов, так почему бы не ссылаться на массив C, если у вас есть? Из-за большого недостатка: вы должны знать размер массива:

void foo(int (&arr)[5])

что является чрезвычайно ограничивающим.

Чтобы обойти это, вам нужно сделать его шаблоном:

template <std::size N>
void foo(int (&arr)[N])

который преследует цель избежать шаблонов, поэтому вам лучше пойти с итераторами шаблона begin + end.


В некоторых случаях (например, математические вычисления только на 2 или 3 значениях, которые имеют одна и та же семантика, поэтому они не должны быть отдельными параметрами) a требуется конкретный размер массива и создание функции generic не имеет смысла. В этих случаях задается размер массива гарантирует безопасность, поскольку позволяет передавать только массив из правильный размер во время компиляции; поэтому он выгоден и не является "большой недостаток"

Одна из красавиц (C и) С++ - это огромный диапазон применимости. Так что да, вы всегда найдете некоторые поля, которые используют или нуждаются в уникальной уникальной функции уникальным образом. Сказав это, даже в вашем примере я все равно уклоняюсь от массивов. Когда у вас есть фиксированное количество значений, которые не должны быть семантически разделены, я думаю, что структура будет правильным выбором по массивам большую часть времени (например, glm::mat4 вместо float[4]).

Но не стоит забывать, что такое std::array: современная замена C-массивов. Одна вещь, которую я узнал при анализе вариантов, заключается в том, что нет абсолютного "лучше, чем". Всегда есть "зависит". Но не в этом случае: std::array должна однозначно заменить C массивы в интерфейсах. Поэтому в редком случае, когда в качестве эталонного параметра необходим контейнер с фиксированным размером, не имеет смысла активизировать использование массивов C, когда у вас уже есть std::array. Таким образом, единственным допустимым случаем, когда подвергать лежащий в основе массив C массива std::array, является потребность в некоторых старых библиотеках, у которых есть ссылочные параметры массива C. Но я думаю, что в большей картине, добавляющей это к интерфейсу, это не оправдано. Новый код должен использовать struct (btw std::tuple становится проще и проще в использовании по каждому стандарту) или std::array.

Ответ 2

Нет.

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

Перепишите код, чтобы принять std::array<T, N>& вместо этого, если это возможно.

Ответ 3

Почему бы не передать std::array.begin()? Работал в SDL2 на:

int SDL_RenderDrawLines(SDL_Renderer *renderer, const SDL_Point *points, int count)

Моя линия будет нарисована:

std::array<SDL_Point, 8> a_line;

Я прошел так:

SDL_RenderDrawLines(r_s_game.p_renderer, a_line.begin(), 8);