Concatenando dois std :: vetores

Como posso concatenar dois std :: vector s?

469
@lecaruyer Você percebe que acabou de marcar uma pergunta que foi feita dois anos antes como uma duplicata
adicionado o autor eshirima, fonte
@Facristian: Não, pode não haver utilidade do ponto de vista da eficiência. A memória vetorial deve ser contínua, então o que você sugere é impossível. Se você quisesse "um compartilhamento sofisticado do gerenciamento dos nós", e se você mudasse a classe de vetor de tal maneira, você acabaria com um deque. Mesmo assim, é muito difícil reutilizar a memória da maneira sugerida, embora ela comece a ser um pouco mais viável. Eu não acho que atualmente está implementado. O principal é que, em tal compartilhamento de nós de gerenciamento (um deque), o nó final pode estar parcialmente vazio.
adicionado o autor Cookie, fonte
As respostas dadas na verdade não se concatenam. Eles anexam uma cópia. Pode haver um uso (para o ponto de vista da eficiência) para criar um método de concatenação std :: vector, no entanto, seria necessário um compartilhamento sofisticado do gerenciamento dos nós e provavelmente é por isso que isso não foi feito.
adicionado o autor FauChristian, fonte
Eu sou o único a perguntar por que isso não é implementado como a + b ou a.concat (b) na biblioteca padrão? Talvez a implementação padrão seja sub-ótima, mas toda concatenação de array não precisa ser micro-otimizada
adicionado o autor oseiskar, fonte

16 Respostas

vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
531
adicionado
Se você tiver concatenando vários vetores para um, é útil chamar reserva no primeiro vetor de destino?
adicionado o autor Faheem Mitha, fonte
@Aidin eu vejo. Obrigado pelo esclarecimento.
adicionado o autor Faheem Mitha, fonte
@ Khaur: Isso é horrível ao ponto de ser difícil de acreditar. É super trivial para uma implementação detectar os iteradores que são acessos aleatórios, calcular o tamanho e pré-reservar o espaço necessário. Eu acho que o MSFT até faz isso para reencaminhar iteradores.
adicionado o autor Mooing Duck, fonte
@AlexanderRafferty: Somente se vector1.capacity ()> = 2 * vector1.size() . Qual é atípico a menos que você tenha chamado std :: vector :: reserve() . Caso contrário, o vetor será realocado, invalidando os iteradores passados ​​como parâmetros 2 e 3.
adicionado o autor Drew Dormann, fonte
Eu tenho uma pergunta. Isso funcionará se vector1 e vector2 forem os mesmos vetores?
adicionado o autor Alexander Rafferty, fonte
Eu adicionaria apenas o código para primeiro obter o número de elementos que cada vetor mantém, e definir o vetor 1 como o que contém o maior. Caso contrário, você está fazendo muitas cópias desnecessárias.
adicionado o autor Joe Pineda, fonte
É uma pena que não exista uma expressão mais sucinta na biblioteca padrão. .concat ou + = ou algo assim
adicionado o autor nmr, fonte
@FaheemMitha: Como os argumentos de insert são vetores, ele já sabe quantos elementos estão à frente e irá lidar com ele mesmo. Se estivéssemos inserindo outras coisas como array, seria útil reservar o espaço primeiro.
adicionado o autor Aidin, fonte
@MooingDuck Você está certo, perdi o despachante e achei que a versão do iterador de entrada se aplicava a todos os tipos de iteradores. A versão do iterador avançado faz muito mais coisas. Obrigado por o apontar, apaguei o meu comentário inicial para que este não apareça sem o seu.
adicionado o autor Khaur, fonte

Eu usaria a função de inserção , algo como:

vector a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());
112
adicionado

Se você estiver usando o C ++ 11 e desejar mover os elementos em vez de apenas copiá-los, use std :: move_iterator ( http://en.cppreference.com/w/cpp/iterator/move_iterator ) junto com insert (ou copy):

#include 
#include 
#include 

int main(int argc, char** argv) {
  std::vector dest{1,2,3,4,5};
  std::vector 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(std::cout, "\n")
    );

  return 0;
}

Isso não será mais eficiente para o exemplo com o ints, já que movê-los não é mais eficiente do que copiá-los, mas para uma estrutura de dados com movimentos otimizados, ele pode evitar a cópia do estado desnecessário:

#include 
#include 
#include 

int main(int argc, char** argv) {
  std::vector> dest{{1,2,3,4,5}, {3,4}};
  std::vector> 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;
}

Após o movimento, o elemento src é deixado em um estado indefinido, mas seguro para destruição, e seus elementos anteriores foram transferidos diretamente para o novo elemento dest no final.

112
adicionado
O método std :: make_move_iterator() me ajudou ao tentar concatenar std :: vectors de std :: unique_ptr.
adicionado o autor Knitschi, fonte

Ou você poderia usar:

std::copy(source.begin(), source.end(), std::back_inserter(destination));

Esse padrão é útil se os dois vetores não contiverem exatamente o mesmo tipo de coisa, porque você pode usar algo em vez de std :: back_inserter para converter de um tipo para outro.

70
adicionado
@Yogesh: concedido, mas não há nada que o impeça de chamar reserve primeiro. A razão por que o std :: copy às vezes é útil é se você quiser usar algo diferente de back_inserter .
adicionado o autor Roger Lipscombe, fonte
Quando você diz "múltiplas alocações", isso é verdade - mas o número de alocações é na pior das hipóteses log (número de entradas adicionadas) - o que significa que o custo de adicionar uma entrada é constante no número de entradas adicionadas. (Basicamente, não se preocupe com isso, a menos que o perfil mostre que você precisa de uma reserva).
adicionado o autor Martin Bonner, fonte
Você pode querer usar std :: transform para fazer isso.
adicionado o autor Martin Broadhurst, fonte
o método de cópia não é tão bom assim. Ele chamará push_back multiple time, o que significa que, se muitos elementos tiverem que ser inseridos, isso pode significar várias realocações. É melhor usar o insert, pois a implementação do vetor poderia fazer alguma otimização para evitar realocações. poderia reservar memória antes de começar a copiar
adicionado o autor Yogesh Arora, fonte
std::vector first;
std::vector second;

first.insert(first.end(), second.begin(), second.end());
30
adicionado

Com o C ++ 11, prefiro seguir para anexar o vetor b a um:

std::move(b.begin(), b.end(), std::back_inserter(a));

quando a e b não são sobrepostos, e b não será mais usado.

27
adicionado
@MartinBonner Obrigado por mencionar isso. Provavelmente eu deveria voltar para o antigo caminho de insert que é mais seguro.
adicionado o autor Deqing, fonte
basta adicionar a seguinte linha de cabeçalho: begin: #include
adicionado o autor Manohar Reddy Poreddy, fonte
Comportamento indefinido se realmente for b (o que é OK se você sabe que isso nunca pode acontecer - mas vale a pena estar ciente no código de propósito geral).
adicionado o autor Martin Bonner, fonte
Ah, o outro std :: move. Bastante confuso na primeira vez que você o vê.
adicionado o autor xaxxon, fonte

Eu prefiro um que já foi mencionado:

a.insert(a.end(), b.begin(), b.end());

Mas se você usar o C ++ 11, existe uma maneira mais genérica:

a.insert(std::end(a), std::begin(b), std::end(b));

Além disso, não faz parte de uma pergunta, mas é aconselhável usar reserva antes de anexar para melhor desempenho. E se você está concatenando vetor com ele mesmo, sem reservá-lo falhar, então você sempre deve reservar .


Então basicamente o que você precisa:

template 
void Append(std::vector& a, const std::vector& b)
{
    a.reserve(a.size() + b.size());
    a.insert(a.end(), b.begin(), b.end());
}
18
adicionado
@Asu ADL só adicionará std :: se o tipo de a vier de std , o que anula o aspecto genérico.
adicionado o autor Potatoswatter, fonte
std :: é deduzido por pesquisa dependente de argumento . end (a) será suficiente.
adicionado o autor Asu, fonte
bom ponto. Nesse caso, é um vetor, então funcionaria de qualquer maneira, mas sim, é uma solução melhor.
adicionado o autor Asu, fonte

Você deve usar o vetor :: inserir

v1.insert(v1.end(), v2.begin(), v2.end());
5
adicionado

Se você estiver interessado em garantia de exceção forte (quando o construtor de cópia pode lançar uma exceção):

template
inline void append_copy(std::vector& v1, const std::vector& 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;
    }
}

O append_move com garantia forte não pode ser implementado em geral se o construtor de movimento do elemento vector puder lançar (o que é improvável, mas ainda assim).

4
adicionado
O insert já lida com isso. Além disso, essa chamada para erase é equivalente a um redimensionar .
adicionado o autor Potatoswatter, fonte
Não é possível que o v1.erase (... lance também?
adicionado o autor camelCase, fonte
vector v1 = {1, 2, 3, 4, 5};
vector v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));
3
adicionado
Embora esse trecho de código possa resolver o problema, ele não explica por que ou como ele responde à pergunta. Por favor, inclua uma explicação para o seu código , pois isso realmente ajuda a melhorar a qualidade do seu post. Sinalizadores/revisores: Para respostas somente de código como esta, não vote, não exclua! (Nota: Esta resposta pode, na verdade, ser simples o suficiente para tornar uma explicação e, portanto, negativas, desnecessárias. Você ainda pode querer adic
adicionado o autor Scott Weldon, fonte

Adicione este ao seu arquivo de cabeçalho:

template  vector concat(vector &a, vector &b) {
    vector ret = vector();
    copy(a.begin(), a.end(), back_inserter(ret));
    copy(b.begin(), b.end(), back_inserter(ret));
    return ret;
}

e use desta forma:

vector a = vector();
vector b = vector();

a.push_back(1);
a.push_back(2);
b.push_back(62);

vector r = concat(a, b);

r irá conter [1,2,62]

2
adicionado
Não sei porque isso foi derrotado. Pode não ser a maneira mais eficiente de fazer isso, mas não é errado e é eficaz.
adicionado o autor leeor_net, fonte

Com o intervalo v3 , você pode ter uma concatenação lazy :

ranges::view::concat(v1, v2)

Demo.

2
adicionado

Aqui está uma solução de uso geral usando a semântica de movimento do C ++ 11:

template 
std::vector concat(const std::vector& lhs, const std::vector& rhs)
{
    if (lhs.empty()) return rhs;
    if (rhs.empty()) return lhs;
    std::vector 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 
std::vector concat(std::vector&& lhs, const std::vector& rhs)
{
    lhs.insert(lhs.cend(), rhs.cbegin(), rhs.cend());
    return std::move(lhs);
}

template 
std::vector concat(const std::vector& lhs, std::vector&& rhs)
{
    rhs.insert(rhs.cbegin(), lhs.cbegin(), lhs.cend());
    return std::move(rhs);
}

template 
std::vector concat(std::vector&& lhs, std::vector&& 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 como isso difere de anexar a um vetor .

1
adicionado

Se o que você está procurando é uma maneira de acrescentar um vetor a outro após a criação, vector :: insert é a sua melhor aposta, como já foi respondido várias vezes, por exemplo:

vector first = {13};
const vector second = {42};

first.insert(first.end(), second.cbegin(), second.cend());

Sadly there's no way to construct a const vector, as above you must construct and then insert.


If what you're actually looking for is a container to hold the concatenation of these two vectors, there may be something better available to you, if:

  1. Seu vetor contém primitivos
  2. Suas primitivas contidas são de tamanho 32 bits ou menor
  3. Você quer um contêiner const

Se os itens acima forem verdadeiros, sugiro usar o basic_string quem é char_type corresponde ao tamanho da primitiva contida no seu vector . Você deve incluir um static_assert em seu código para valide esses tamanhos para ficar consistente:

static_assert(sizeof(char32_t) == sizeof(int));

Com esta verdadeira verdade, você pode apenas fazer:

const u32string concatenation = u32string(first.cbegin(), first.cend()) + u32string(second.cbegin(), second.cend());

For more information on the differences between string and vector you can look here: https://stackoverflow.com/a/35558008/2642059

For a live example of this code you can look here: http://ideone.com/7Iww3I

0
adicionado

Para ser honesto, você pode rapidamente concatenar dois vetores copiando elementos de dois vetores para o outro ou apenas acrescentar um dos dois vetores. Depende do seu objetivo.

Method 1: Assign new vector with its size is the sum of two original vectors' size.

vector concat_vector = vector();
concat_vector.setcapacity(vector_A.size() + vector_B.size());
// Loop for copy elements in two vectors into concat_vector

Method 2: Append vector A by adding/inserting elements of 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());
0
adicionado
O que sua resposta acrescenta que ainda não foi fornecida em outras respostas?
adicionado o autor Mat, fonte
Se o (s) vector (s) original (eis) não são mais necessários depois, pode ser melhor usar std :: move_iterator para que elementos sejam movidos ao invés de copiados. (veja en.cppreference.com/w/cpp/iterator/move_iterator ) .
adicionado o autor tmlen, fonte
@ Mat: caracteres em negrito.
adicionado o autor marcv81, fonte

Um aumento de desempenho geral para concatenar é verificar o tamanho dos vetores. E mesclar/inserir o menor com o maior.

//vector v1,v2;
if(v1.size()>v2.size()){
    v1.insert(v1.end(),v2.begin(),v2.end());
}else{
    v1.insert(v2.end(),v1.begin(),v1.end());
}
0
adicionado