Я использую внешнюю библиотеку, которая в какой-то момент дает мне необработанный указатель на массив целых чисел и размер.
Теперь я хотел бы использовать std::vector
для доступа и изменения этих значений на месте, а не доступ к ним с помощью необработанных указателей.
Вот примерный пример, который объясняет суть:
size_t size = 0;
int * data = get_data_from_library(size); // raw data from library {5,3,2,1,4}, size gets filled in
std::vector<int> v = ????; // pseudo vector to be used to access the raw data
std::sort(v.begin(), v.end()); // sort raw data in place
for (int i = 0; i < 5; i++)
{
std::cout << data[i] << "\n"; // display sorted raw data
}
Ожидаемый результат:
1
2
3
4
5
Причина в том, что мне нужно применить алгоритмы <algorithm>
(сортировка, замена элементов и т. Д.) К этим данным.
С другой стороны изменения размера этого вектора рука никогда не будет изменена, поэтому push_back
, erase
, insert
не обязаны работать на этом векторе.
Я мог бы построить вектор на основе данных из библиотеки, использовать модифицировать этот вектор и копировать данные обратно в библиотеку, но это были бы две полные копии, которых я хотел бы избежать, поскольку набор данных мог бы быть действительно большим.
std::vector_view
, не так ли?std::vector
работает.sort(arrayPointer, arrayPointer + elementCount);
.Ответы:
Проблема заключается в том, что
std::vector
необходимо сделать копию элементов из массива, которым вы его инициализируете, поскольку он владеет объектами, которые он содержит.Чтобы избежать этого, вы можете использовать объект среза для массива (то есть аналогично тому, что
std::string_view
нужноstd::string
). Вы можете написать свою собственнуюarray_view
реализацию шаблона класса, чьи экземпляры создаются, беря необработанный указатель на первый элемент массива и длину массива:array_view
не хранит массив; он просто содержит указатель на начало массива и длину этого массива. Следовательно,array_view
объекты дешевы в создании и копировании.Поскольку
array_view
обеспечиваетbegin()
иend()
члены функции, вы можете использовать стандартные алгоритмы библиотеки (например,std::sort
,std::find
,std::lower_bound
и т.д.) на нем:Вывод:
Используйте
std::span
(илиgsl::span
) вместоПриведенная выше реализация раскрывает концепцию объектов слайса . Тем не менее, начиная с C ++ 20 вы можете использовать напрямую
std::span
. В любом случае вы можете использоватьgsl::span
начиная с C ++ 14.источник
C ++ 20-
std::span
Если вы можете использовать C ++ 20, вы можете использовать
std::span
пару указателей и длин, которая дает пользователю представление о непрерывной последовательности элементов. Это своего родаstd::string_view
, и в то время какstd::span
иstd::string_view
являются представлениями, ониstd::string_view
доступны только для чтения.Из документов:
Таким образом, будет работать следующее:
Проверьте это в прямом эфире
Поскольку
std::span
в основном это пара указатель - длина, вы также можете использовать ее следующим образом:Примечание: не все компиляторы поддерживают
std::span
. Проверьте поддержку компилятора здесь .ОБНОВИТЬ
Если вы не можете использовать C ++ 20, вы можете использовать его,
gsl::span
который в основном является базовой версией стандарта C ++std::span
.Решение C ++ 11
Если вы ограничены стандартом C ++ 11, вы можете попробовать реализовать свой собственный простой
span
класс:Проверьте версию C ++ 11 вживую
источник
gsl::span
для C ++ 14 и выше, если ваш компилятор не реализуетstd::span
Поскольку библиотека алгоритмов работает с итераторами, вы можете сохранить массив.
Для указателей и известной длины массива
Здесь вы можете использовать необработанные указатели в качестве итераторов. Они поддерживают все операции, которые поддерживает итератор (приращение, сравнение на равенство, значение и т. Д.):
data
указывает на элемент самого первого массива как итератор, возвращаемыйbegin()
иdata + size
указывает на элемент после последнего элемента массива, как возвращаемый итераторend()
.Для массивов
Здесь вы можете использовать
std::begin()
иstd::end()
Но имейте в виду, что это работает, только если
data
не распадается на указатель, потому что тогда информация о длине пропадает.источник
Вы можете получить итераторы для необработанных массивов и использовать их в алгоритмах:
Если вы работаете с необработанными указателями (ptr + size), вы можете использовать следующую технику:
UPD: Однако приведенный выше пример имеет плохой дизайн. Библиотека возвращает нам необработанный указатель, и мы не знаем, где расположен основной буфер и кто должен его освобождать.
Обычно вызывающая сторона предоставляет буферизованную функцию для заполнения данных. В этом случае мы можем предварительно выделить вектор и использовать его базовый буфер:
При использовании C ++ 11 или выше мы можем даже сделать get_data_from_library (), чтобы вернуть вектор. Благодаря операциям перемещения, не будет копии памяти.
источник
auto begin = data;
auto end = data + size;
get_data_from_library()
? Может быть, мы не должны менять это вообще. Если нам нужно передать буфер в библиотеку, мы можем выделить вектор и передатьv.data()
Вы не можете сделать это с
std::vector
без копирования.std::vector
владеет указателем, который находится у него под капотом, и распределяет пространство через предоставленный распределитель.Если у вас есть доступ к компилятору, который поддерживает C ++ 20, вы можете использовать std :: span, который был создан именно для этой цели. Он заключает указатель и размер в «контейнер», который имеет интерфейс контейнера C ++.
Если нет, вы можете использовать gsl :: span, на котором была основана стандартная версия.
Если вы не хотите импортировать другую библиотеку, вы можете легко реализовать ее самостоятельно в зависимости от того, какую функциональность вы хотите иметь.
источник
Тебе нельзя. Это не то
std::vector
, для чего.std::vector
управляет своим собственным буфером, который всегда получается из распределителя. Он никогда не становится владельцем другого буфера (за исключением другого вектора того же типа).С другой стороны, вам также не нужно, потому что ...
Эти алгоритмы работают на итераторах. Указатель является итератором массива. Вам не нужен вектор:
В отличие от шаблонов функций
<algorithm>
, некоторые инструменты, такие как range-for иstd::begin
/std::end
и диапазоны C ++ 20, не работают только с парой итераторов, хотя работают с контейнерами, такими как векторы. Можно создать класс-оболочку для iterator + size, который ведет себя как диапазон и работает с этими инструментами. C ++ 20 будет ввести такую обертку в стандартную библиотеку:std::span
.источник
Помимо другого хорошего предложения о
std::span
переходе на c ++ 20 иgsl:span
включении вашего собственного (легковесного)span
класса до тех пор, это уже достаточно просто (не стесняйтесь копировать):Особого внимания заслуживает также расширенный диапазон библиотеки диапазонов усиления, если вас интересует более общая концепция диапазона: https://www.boost.org/doc/libs/1_60_0/libs/range/doc/html/range/reference /utilities/iterator_range.html .
Концепции диапазона также появятся в C ++ 20
источник
using value_type = std::remove_cv_t<T>;
?span(T* first_, size_t length) : first(first), length(length) {};
. Я отредактировал твой ответ.using value_type = std::remove_cv_t<T>;
главным образом необходимо, если используется с шаблонным программированием (для получения value_type для 'range'). Если вы просто хотите использовать итераторы, вы можете пропустить / удалить это.Вы фактически могли бы почти использовать
std::vector
это, злоупотребляя пользовательскими функциями распределителя, чтобы возвращать указатель на память, которую вы хотите просмотреть. Это не гарантируется стандартом для работы (заполнение, выравнивание, инициализация возвращаемых значений; вам придется приложить усилия при назначении начального размера, а для не примитивов вам также понадобится взломать ваши конструкторы ), но на практике я бы ожидал, что это дало достаточно настроек.Никогда и никогда не делай этого. Это уродливо, удивительно, нахально и ненужно. Алгоритмы стандартной библиотеки уже разработаны для работы как с необработанными массивами, так и с векторами. Смотрите другие ответы для деталей этого.
источник
vector
конструкторами, которые принимают пользовательскую ссылку на Allocator в качестве аргумента конструктора (а не просто параметр шаблона). Я думаю, вам понадобится объект распределителя, в котором есть значение указателя времени выполнения, а не как параметр шаблона, иначе он может работать только для адресов constexpr. Вы должны быть осторожны, чтобы неvector
создавать объекты по умолчанию.resize()
и не перезаписывать существующие данные; несоответствие между владеющим контейнером , как вектор против не владеющей пяди огромно , если вы начнете использовать .push_back и т.д.construct
метод, который был бы необходим ... Я не могу представить, какие нехакерные варианты использования потребовали бы этого вместо размещения нового.resize()
перед тем, как передать ссылку на что-то, что хочет использовать ее в качестве чистого вывода (например, системный вызов read). На практике компиляторы часто не оптимизируют этот memset или что-то еще. Или, если бы у вас был распределитель, который использует calloc для получения предварительно обнуленной памяти, вы также можете избежать его загрязнения, как этоstd::vector<int>
делает глупость по умолчанию при создании объектов по умолчанию, которые имеют шаблон с нулевым битом. См. Примечания в en.cppreference.com/w/cpp/container/vector/vectorКак уже отмечали другие, они
std::vector
должны владеть основной памятью (если не возиться с пользовательским распределителем), поэтому ее нельзя использовать.Другие также рекомендовали диапазон c ++ 20, однако очевидно, что для этого требуется c ++ 20.
Я бы порекомендовал span-lite span. Процитирую это субтитры:
Он обеспечивает не владеющее и изменяемое представление (поскольку вы можете изменять элементы и их порядок, но не вставлять их), и, как говорится в цитате, не имеет зависимостей и работает на большинстве компиляторов.
Ваш пример:
Печать
Это также имеет дополнительное вверх , если один день вы делаете переход на C ++ 20, вы просто должны быть в состоянии заменить это
nonstd::span
сstd::span
.источник
Вы можете использовать
std::reference_wrapper
доступные начиная с C ++ 11:источник
std::copy(std::begin(src_table), std::end(src_table), std::back_inserter(dest_vector));
определенно заполняетdest_vector
значения, взятые изsrc_table
(IOW, в которые копируются данныеdest_vector
), поэтому я не получил ваш комментарий. Могли бы вы объяснить?