Estoy usando una biblioteca externa que en algún momento me da un puntero en bruto a una matriz de enteros y un tamaño.
Ahora me gustaría utilizar std::vector
para acceder y modificar estos valores en su lugar, en lugar de acceder a ellos con punteros sin formato.
Aquí hay un ejemplo articular que explica el punto:
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
}
Rendimiento esperado:
1
2
3
4
5
La razón es que necesito aplicar algoritmos de <algorithm>
(clasificación, intercambio de elementos, etc.) en esos datos.
Por otro lado cambiar el tamaño de ese vector no sería cambiado, por lo que push_back
, erase
, insert
no están obligados a trabajar en ese vector.
Podría construir un vector basado en los datos de la biblioteca, usar modificar ese vector y copiar los datos de nuevo a la biblioteca, pero serían dos copias completas que me gustaría evitar ya que el conjunto de datos podría ser realmente grande.
std::vector_view
, ¿no?std::vector
funciona.sort(arrayPointer, arrayPointer + elementCount);
.Respuestas:
El problema es que
std::vector
tiene que hacer una copia de los elementos de la matriz con la que lo inicializa, ya que tiene la propiedad de los objetos que contiene.Para evitar esto, puede usar un objeto de división para una matriz (es decir, similar a lo que
std::string_view
esstd::string
). Puede escribir su propiaarray_view
implementación de plantilla de clase cuyas instancias se construyen tomando un puntero sin procesar al primer elemento de una matriz y la longitud de la matriz:array_view
no almacena una matriz; solo tiene un puntero al comienzo de la matriz y la longitud de esa matriz. Por lo tanto, losarray_view
objetos son baratos de construir y copiar.Dado que
array_view
proporciona lasbegin()
yend()
miembros de funciones, se pueden utilizar los algoritmos de la biblioteca estándar (por ejemplo,std::sort
,std::find
,std::lower_bound
, etc.) sobre el mismo:Salida:
Use
std::span
(ogsl::span
) en su lugarLa implementación anterior expone el concepto detrás de los objetos de corte . Sin embargo, desde C ++ 20 puede usar directamente en su
std::span
lugar. En cualquier caso, puedes usarlogsl::span
desde C ++ 14.fuente
C ++ 20's
std::span
Si puede usar C ++ 20, puede usar
std::span
un par de longitud de puntero que le da al usuario una vista de una secuencia contigua de elementos. Es una especie destd::string_view
, y mientras tantostd::span
ystd::string_view
son vistas no propietario,std::string_view
es una vista de sólo lectura.De los documentos:
Entonces lo siguiente funcionaría:
Compruébalo en vivo
Dado que
std::span
es básicamente un par de puntero - longitud, también puede usarlo de la siguiente manera:Nota: No todos los compiladores son compatibles
std::span
. Verifique el soporte del compilador aquí .ACTUALIZAR
Si no puede usar C ++ 20, podría usar
gsl::span
básicamente la versión base de los estándares de C ++std::span
.Solución C ++ 11
Si está limitado al estándar C ++ 11, puede intentar implementar su propia
span
clase simple :Echa un vistazo a la versión C ++ 11 en vivo
fuente
gsl::span
para C ++ 14 y superior si su compilador no se implementastd::span
Dado que la biblioteca de algoritmos funciona con iteradores, puede mantener la matriz.
Para punteros y longitud de matriz conocida
Aquí puede usar punteros sin formato como iteradores. Admiten todas las operaciones que admite un iterador (incremento, comparación para igualdad, valor de, etc.):
data
señala al miembro de la matriz dirst como un iterador devuelto porbegin()
ydata + size
apunta al elemento después del último elemento de la matriz como un iterador devuelto porend()
.Para matrices
Aquí puedes usar
std::begin()
ystd::end()
Pero tenga en cuenta que esto solo funciona, si
data
no se descompone en un puntero, porque la información de longitud se pierde.fuente
Puede obtener iteradores en matrices sin procesar y usarlos en algoritmos:
Si está trabajando con punteros sin formato (ptr + tamaño), puede usar la siguiente técnica:
UPD: Sin embargo, el ejemplo anterior es de mal diseño. La biblioteca nos devuelve un puntero sin procesar y no sabemos dónde está asignado el búfer subyacente y quién se supone que lo liberará.
Por lo general, la persona que llama proporciona un búfer para que la función complete los datos. En ese caso, podemos preasignar el vector y usar su búfer subyacente:
Al usar C ++ 11 o superior, incluso podemos hacer que get_data_from_library () devuelva un vector. Gracias a las operaciones de movimiento, no habrá copia de memoria.
fuente
auto begin = data;
auto end = data + size;
get_data_from_library()
se asignan los datos devueltos por ? Tal vez no debemos cambiarlo en absoluto. Si necesitamos pasar un búfer a la biblioteca, entonces podemos asignar el vector y pasarv.data()
No puede hacer esto con un
std::vector
sin hacer una copia.std::vector
posee el puntero que tiene debajo del capó y asigna espacio a través del asignador que se proporciona.Si tiene acceso a un compilador que tiene soporte para C ++ 20, puede usar std :: span que fue construido exactamente para este propósito. Envuelve un puntero y un tamaño en un "contenedor" que tiene la interfaz de contenedor C ++.
Si no, puede usar gsl :: span, que es en lo que se basó la versión estándar.
Si no desea importar otra biblioteca, puede implementarla trivialmente usted mismo dependiendo de qué funcionalidad quiera tener.
fuente
No se puede. Eso no
std::vector
es para lo que sirve.std::vector
gestiona su propio búfer, que siempre se adquiere de un asignador. Nunca toma posesión de otro búfer (excepto de otro vector del mismo tipo).Por otro lado, tampoco es necesario porque ...
Esos algoritmos funcionan en iteradores. Un puntero es un iterador de una matriz. No necesitas un vector:
A diferencia de las plantillas de función en
<algorithm>
, algunas herramientas como el rango parastd::begin
/ /std::end
y los rangos C ++ 20 no funcionan con solo un par de iteradores, aunque sí funcionan con contenedores como los vectores. Es posible crear una clase de contenedor para iterador + tamaño que se comporte como un rango y funcione con estas herramientas. C ++ 20 introducirá tales envoltura en la biblioteca estándar:std::span
.fuente
Además de la otra buena sugerencia sobre
std::span
venir en c ++ 20 egsl:span
incluir su propiaspan
clase (ligera) hasta entonces ya es bastante fácil (siéntase libre de copiar):De especial interés es también la biblioteca boost range boost-range si está interesado en el concepto de rango más genérico: https://www.boost.org/doc/libs/1_60_0/libs/range/doc/html/range/reference /utilities/iterator_range.html .
Los conceptos de rango también llegarán en c ++ 20
fuente
using value_type = std::remove_cv_t<T>;
de?span(T* first_, size_t length) : first(first), length(length) {};
. Edité tu respuesta.using value_type = std::remove_cv_t<T>;
necesita principalmente si se usa con la programación de plantillas (para obtener el value_type de un 'rango'). Si solo desea utilizar los iteradores, puede omitir / eliminar eso.En realidad casi podrías usar
std::vector
esto, abusando de la funcionalidad del asignador personalizado para devolver un puntero a la memoria que desea ver. Eso no estaría garantizado por el estándar para trabajar (relleno, alineación, inicialización de los valores devueltos; tendrías que esforzarte al asignar el tamaño inicial, y para los no primitivos también necesitarías hackear tus constructores ), pero en la práctica esperaría que le dieran suficientes ajustes.Nunca jamás hagas eso. Es feo, sorprendente, hacky e innecesario. Los algoritmos de la biblioteca estándar ya están diseñados para funcionar tan bien con matrices sin procesar como con vectores. Vea las otras respuestas para detalles de eso.
fuente
vector
constructores que toman una referencia de asignación personalizada como un constructor arg (no solo un parámetro de plantilla). Supongo que necesitaría un objeto asignador que tuviera el valor del puntero en tiempo de ejecución, no como un parámetro de plantilla; de lo contrario, solo podría funcionar para direcciones constexpr. Tendría que tener cuidado de no permitir quevector
los objetos de construcción predeterminados.resize()
ni sobrescriban los datos existentes; el desajuste entre la posesión de un recipiente como vector frente a no poseer palmo es enorme si se inicia mediante .push_back etcconstruct
método que se requeriría ... No puedo pensar qué casos de uso no hacky requerirían que la colocación fuera nueva.resize()
antes de pasar una referencia a algo que quiere usarlo como una salida pura (por ejemplo, una llamada al sistema de lectura). En la práctica, los compiladores a menudo no optimizan ese memset o lo que sea. O si tuviera un asignador que usa calloc para obtener memoria pre-puesta a cero, también podría evitar ensuciarlo de la manera estúpidastd::vector<int>
por defecto cuando construye objetos por defecto que tienen un patrón de bits cero. Vea las notas en es.cppreference.com/w/cpp/container/vector/vectorComo otros han señalado,
std::vector
debe ser dueño de la memoria subyacente (menos que jugar con un asignador personalizado) para que no se pueda usar.Otros también han recomendado la duración de c ++ 20, sin embargo, obviamente eso requiere c ++ 20.
Recomendaría el span-lite span. Para citar es subtítulo:
Proporciona una vista no propietaria y mutable (ya que puede mutar elementos y su orden pero no insertarlos) y, como dice la cita, no tiene dependencias y funciona en la mayoría de los compiladores.
Su ejemplo:
Huellas dactilares
Esto también tiene la ventaja adicional si un día cambias a c ++ 20, solo deberías poder reemplazarlo
nonstd::span
porstd::span
.fuente
Puede usar un
std::reference_wrapper
disponible desde C ++ 11:fuente
std::copy(std::begin(src_table), std::end(src_table), std::back_inserter(dest_vector));
definitivamente llena losdest_vector
valores tomados desrc_table
(IOW se copian los datosdest_vector
), por lo que no recibí tu comentario. ¿Podrías explicar?