¿Cómo concateno dos std::vector
s?
a + b
o a.concat(b)
en la biblioteca estándar? Tal vez la implementación predeterminada sería subóptima, pero no es necesario que cada concatenación de matriz sea micro-optimizada
¿Cómo concateno dos std::vector
s?
a + b
o a.concat(b)
en la biblioteca estándar? Tal vez la implementación predeterminada sería subóptima, pero no es necesario que cada concatenación de matriz sea micro-optimizada
Respuestas:
vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
reserve
primero al vector de destino?
vector1.capacity() >= 2 * vector1.size()
. Lo cual es atípico a menos que haya llamado std::vector::reserve()
. De lo contrario, el vector se reasignará, invalidando los iteradores pasados como parámetros 2 y 3.
.concat
o +=
algo así
Si está utilizando C ++ 11 y desea mover los elementos en lugar de simplemente copiarlos, puede usar std::move_iterator
junto con insertar (o copiar):
#include <vector>
#include <iostream>
#include <iterator>
int main(int argc, char** argv) {
std::vector<int> dest{1,2,3,4,5};
std::vector<int> src{6,7,8,9,10};
// Move elements from src to dest.
// src is left in undefined but safe-to-destruct state.
dest.insert(
dest.end(),
std::make_move_iterator(src.begin()),
std::make_move_iterator(src.end())
);
// Print out concatenated vector.
std::copy(
dest.begin(),
dest.end(),
std::ostream_iterator<int>(std::cout, "\n")
);
return 0;
}
Esto no será más eficiente para el ejemplo con ints, ya que moverlos no es más eficiente que copiarlos, pero para una estructura de datos con movimientos optimizados, puede evitar copiar estados innecesarios:
#include <vector>
#include <iostream>
#include <iterator>
int main(int argc, char** argv) {
std::vector<std::vector<int>> dest{{1,2,3,4,5}, {3,4}};
std::vector<std::vector<int>> src{{6,7,8,9,10}};
// Move elements from src to dest.
// src is left in undefined but safe-to-destruct state.
dest.insert(
dest.end(),
std::make_move_iterator(src.begin()),
std::make_move_iterator(src.end())
);
return 0;
}
Después del movimiento, el elemento src se deja en un estado indefinido pero seguro para destruir, y sus elementos anteriores se transfirieron directamente al nuevo elemento de dest al final.
std::move(src.begin(), src.end(), back_inserter(dest))
?
Me gustaría utilizar la función de inserción , algo así como:
vector<int> a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());
O podrías usar:
std::copy(source.begin(), source.end(), std::back_inserter(destination));
Este patrón es útil si los dos vectores no contienen exactamente el mismo tipo de cosa, porque puede usar algo en lugar de std :: back_inserter para convertir de un tipo a otro.
reserve
primero. La razón a std::copy
veces es útil si desea utilizar algo distinto de back_inserter
.
Con C ++ 11, preferiría seguir para agregar el vector b a:
std::move(b.begin(), b.end(), std::back_inserter(a));
cuando a
y b
no se superponen, y b
ya no se va a usar.
Esto es std::move
de <algorithm>
, no el habitual std::move
de <utility>
.
insert
forma que es más segura.
insert()
con move_iterator
s? ¿Si es así, cómo?
std::move
que estamos hablando aquí, ya que la mayoría de las personas no conocen esta sobrecarga. Espero que sea una mejora.
std::vector<int> first;
std::vector<int> second;
first.insert(first.end(), second.begin(), second.end());
Prefiero uno que ya se menciona:
a.insert(a.end(), b.begin(), b.end());
Pero si usa C ++ 11, hay una forma más genérica:
a.insert(std::end(a), std::begin(b), std::end(b));
Además, no es parte de una pregunta, pero es aconsejable usar reserve
antes de agregar para un mejor rendimiento. Y si está concatenando un vector consigo mismo, sin reservarlo falla, por lo que siempre debería hacerlo reserve
.
Básicamente lo que necesitas:
template <typename T>
void Append(std::vector<T>& a, const std::vector<T>& b)
{
a.reserve(a.size() + b.size());
a.insert(a.end(), b.begin(), b.end());
}
std::
si el tipo de a
proviene std
, lo que anula el aspecto genérico.
Con el rango v3 , puede tener una concatenación perezosa :
ranges::view::concat(v1, v2)
Deberías usar vector :: insert
v1.insert(v1.end(), v2.begin(), v2.end());
Un aumento de rendimiento general para concatenar es verificar el tamaño de los vectores. E fusionar / insertar el más pequeño con el más grande.
//vector<int> v1,v2;
if(v1.size()>v2.size()) {
v1.insert(v1.end(),v2.begin(),v2.end());
} else {
v2.insert(v2.end(),v1.begin(),v1.end());
}
v1.insert(v2.end()...
está utilizando un iterador en v2
para especificar la posición en v1
.
Si desea poder concatenar vectores de forma concisa, puede sobrecargar el +=
operador.
template <typename T>
std::vector<T>& operator +=(std::vector<T>& vector1, const std::vector<T>& vector2) {
vector1.insert(vector1.end(), vector2.begin(), vector2.end());
return vector1;
}
Entonces puedes llamarlo así:
vector1 += vector2;
Si está interesado en una garantía de excepción fuerte (cuando el constructor de copias puede lanzar una excepción):
template<typename T>
inline void append_copy(std::vector<T>& v1, const std::vector<T>& v2)
{
const auto orig_v1_size = v1.size();
v1.reserve(orig_v1_size + v2.size());
try
{
v1.insert(v1.end(), v2.begin(), v2.end());
}
catch(...)
{
v1.erase(v1.begin() + orig_v1_size, v1.end());
throw;
}
}
Similar append_move
con una fuerte garantía no se puede implementar en general si el constructor de movimiento del elemento vector puede lanzar (lo cual es poco probable pero aún así).
v1.erase(...
lanzar también?
insert
Ya maneja esto. Además, esta llamada a erase
es equivalente a a resize
.
Agregue este a su archivo de encabezado:
template <typename T> vector<T> concat(vector<T> &a, vector<T> &b) {
vector<T> ret = vector<T>();
copy(a.begin(), a.end(), back_inserter(ret));
copy(b.begin(), b.end(), back_inserter(ret));
return ret;
}
y úsalo de esta manera:
vector<int> a = vector<int>();
vector<int> b = vector<int>();
a.push_back(1);
a.push_back(2);
b.push_back(62);
vector<int> r = concat(a, b);
r contendrá [1,2,62]
Aquí hay una solución de uso general que utiliza semántica de movimiento C ++ 11:
template <typename T>
std::vector<T> concat(const std::vector<T>& lhs, const std::vector<T>& rhs)
{
if (lhs.empty()) return rhs;
if (rhs.empty()) return lhs;
std::vector<T> result {};
result.reserve(lhs.size() + rhs.size());
result.insert(result.cend(), lhs.cbegin(), lhs.cend());
result.insert(result.cend(), rhs.cbegin(), rhs.cend());
return result;
}
template <typename T>
std::vector<T> concat(std::vector<T>&& lhs, const std::vector<T>& rhs)
{
lhs.insert(lhs.cend(), rhs.cbegin(), rhs.cend());
return std::move(lhs);
}
template <typename T>
std::vector<T> concat(const std::vector<T>& lhs, std::vector<T>&& rhs)
{
rhs.insert(rhs.cbegin(), lhs.cbegin(), lhs.cend());
return std::move(rhs);
}
template <typename T>
std::vector<T> concat(std::vector<T>&& lhs, std::vector<T>&& rhs)
{
if (lhs.empty()) return std::move(rhs);
lhs.insert(lhs.cend(), std::make_move_iterator(rhs.begin()), std::make_move_iterator(rhs.end()));
return std::move(lhs);
}
Observe cómo esto difiere de append
ing a a vector
.
Puede preparar su propia plantilla para el operador +:
template <typename T>
inline T operator+(const T & a, const T & b)
{
T res = a;
res.insert(res.end(), b.begin(), b.end());
return res;
}
Lo siguiente: solo usa +:
vector<int> a{1, 2, 3, 4};
vector<int> b{5, 6, 7, 8};
for (auto x: a + b)
cout << x << " ";
cout << endl;
Este ejemplo da salida:
1 2 3 4 5 6 7 8
T operator+(const T & a, const T & b)
es peligroso, es mejor usarlo vector<T> operator+(const vector<T> & a, const vector<T> & b)
.
Existe un algoritmo std::merge
de C ++ 17 , que es muy fácil de usar,
A continuación se muestra el ejemplo:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
//DATA
std::vector<int> v1{2,4,6,8};
std::vector<int> v2{12,14,16,18};
//MERGE
std::vector<int> dst;
std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(dst));
//PRINT
for(auto item:dst)
std::cout<<item<<" ";
return 0;
}
std::vector::insert
, pero hace algo diferente: fusionar dos rangos en un nuevo rango versus insertar un vector al final de otro. Vale la pena mencionar en la respuesta?
Si su objetivo es simplemente iterar sobre el rango de valores para fines de solo lectura, una alternativa es envolver ambos vectores alrededor de un proxy (O (1)) en lugar de copiarlos (O (n)), para que se vean rápidamente como una sola, contigua.
std::vector<int> A{ 1, 2, 3, 4, 5};
std::vector<int> B{ 10, 20, 30 };
VecProxy<int> AB(A, B); // ----> O(1)!
for (size_t i = 0; i < AB.size(); i++)
std::cout << AB[i] << " "; // ----> 1 2 3 4 5 10 20 30
Consulte https://stackoverflow.com/a/55838758/2379625 para obtener más detalles, incluida la implementación 'VecProxy', así como los pros y los contras.
vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));
He implementado esta función que concatena cualquier cantidad de contenedores, pasando de rvalue-references y copiando lo contrario
namespace internal {
// Implementation detail of Concatenate, appends to a pre-reserved vector, copying or moving if
// appropriate
template<typename Target, typename Head, typename... Tail>
void AppendNoReserve(Target* target, Head&& head, Tail&&... tail) {
// Currently, require each homogenous inputs. If there is demand, we could probably implement a
// version that outputs a vector whose value_type is the common_type of all the containers
// passed to it, and call it ConvertingConcatenate.
static_assert(
std::is_same_v<
typename std::decay_t<Target>::value_type,
typename std::decay_t<Head>::value_type>,
"Concatenate requires each container passed to it to have the same value_type");
if constexpr (std::is_lvalue_reference_v<Head>) {
std::copy(head.begin(), head.end(), std::back_inserter(*target));
} else {
std::move(head.begin(), head.end(), std::back_inserter(*target));
}
if constexpr (sizeof...(Tail) > 0) {
AppendNoReserve(target, std::forward<Tail>(tail)...);
}
}
template<typename Head, typename... Tail>
size_t TotalSize(const Head& head, const Tail&... tail) {
if constexpr (sizeof...(Tail) > 0) {
return head.size() + TotalSize(tail...);
} else {
return head.size();
}
}
} // namespace internal
/// Concatenate the provided containers into a single vector. Moves from rvalue references, copies
/// otherwise.
template<typename Head, typename... Tail>
auto Concatenate(Head&& head, Tail&&... tail) {
size_t totalSize = internal::TotalSize(head, tail...);
std::vector<typename std::decay_t<Head>::value_type> result;
result.reserve(totalSize);
internal::AppendNoReserve(&result, std::forward<Head>(head), std::forward<Tail>(tail)...);
return result;
}
Si lo que está buscando es una forma de agregar un vector a otro después de la creación, vector::insert
es su mejor opción, como se ha respondido varias veces, por ejemplo:
vector<int> first = {13};
const vector<int> second = {42};
first.insert(first.end(), second.cbegin(), second.cend());
Lamentablemente no hay forma de construir un const vector<int>
, como se debe construir arriba y luego insert
.
Si lo que realmente está buscando es un contenedor para contener la concatenación de estos dos vector<int>
s, puede haber algo mejor disponible para usted, si:
vector
contiene primitivasconst
contenedorSi todo lo anterior es cierto, te sugiero que uses basic_string
quién char_type
coincide con el tamaño de la primitiva contenida en tu vector
. Debe incluir un static_assert
en su código para validar que estos tamaños se mantengan consistentes:
static_assert(sizeof(char32_t) == sizeof(int));
Con esto es cierto, puedes hacer:
const u32string concatenation = u32string(first.cbegin(), first.cend()) + u32string(second.cbegin(), second.cend());
Para obtener más información sobre las diferencias entre string
y vector
puede mirar aquí: https://stackoverflow.com/a/35558008/2642059
Para ver un ejemplo en vivo de este código, puede consultar aquí: http://ideone.com/7Iww3I
Esta solución puede ser un poco complicada, pero boost-range
también tiene otras cosas buenas que ofrecer.
#include <iostream>
#include <vector>
#include <boost/range/algorithm/copy.hpp>
int main(int, char**) {
std::vector<int> a = { 1,2,3 };
std::vector<int> b = { 4,5,6 };
boost::copy(b, std::back_inserter(a));
for (auto& iter : a) {
std::cout << iter << " ";
}
return EXIT_SUCCESS;
}
A menudo, la intención es combinar el vector a
y b
simplemente iterar sobre él haciendo alguna operación. En este caso, existe la ridícula join
función simple .
#include <iostream>
#include <vector>
#include <boost/range/join.hpp>
#include <boost/range/algorithm/copy.hpp>
int main(int, char**) {
std::vector<int> a = { 1,2,3 };
std::vector<int> b = { 4,5,6 };
std::vector<int> c = { 7,8,9 };
// Just creates an iterator
for (auto& iter : boost::join(a, boost::join(b, c))) {
std::cout << iter << " ";
}
std::cout << "\n";
// Can also be used to create a copy
std::vector<int> d;
boost::copy(boost::join(a, boost::join(b, c)), std::back_inserter(d));
for (auto& iter : d) {
std::cout << iter << " ";
}
return EXIT_SUCCESS;
}
Para vectores grandes, esto podría ser una ventaja, ya que no hay copia. También se puede usar para copiar y generalizar fácilmente en más de un contenedor.
Por alguna razón no hay nada como boost::join(a,b,c)
, lo que podría ser razonable.
Puede hacerlo con algoritmos STL pre-implementados usando una plantilla para un uso de tipo polimórfico.
#include <iostream>
#include <vector>
#include <algorithm>
template<typename T>
void concat(std::vector<T>& valuesa, std::vector<T>& valuesb){
for_each(valuesb.begin(), valuesb.end(), [&](int value){ valuesa.push_back(value);});
}
int main()
{
std::vector<int> values_p={1,2,3,4,5};
std::vector<int> values_s={6,7};
concat(values_p, values_s);
for(auto& it : values_p){
std::cout<<it<<std::endl;
}
return 0;
}
Puede borrar el segundo vector si no desea usarlo más ( clear()
método).
Para ser honesto, ¡podría concatenar rápidamente dos vectores copiando elementos de dos vectores en el otro o simplemente agregar uno de los dos vectores! Depende de tu puntería.
Método 1: asignar un nuevo vector con su tamaño es la suma del tamaño de dos vectores originales.
vector<int> concat_vector = vector<int>();
concat_vector.setcapacity(vector_A.size() + vector_B.size());
// Loop for copy elements in two vectors into concat_vector
Método 2: Agregue el vector A agregando / insertando elementos del vector B.
// Loop for insert elements of vector_B into vector_A with insert()
function: vector_A.insert(vector_A .end(), vector_B.cbegin(), vector_B.cend());
std::move_iterator
para que los elementos se muevan en lugar de copiarse. (ver en.cppreference.com/w/cpp/iterator/move_iterator ).
setcapacity
? ¿Qué es function:
?
resize
método.