Como você copia o conteúdo de uma matriz para um std :: vector em C ++ sem loop?

122

Eu tenho uma matriz de valores que é passada para minha função de uma parte diferente do programa que preciso armazenar para processamento posterior. Como não sei quantas vezes minha função será chamada antes da hora de processar os dados, preciso de uma estrutura de armazenamento dinâmica, então escolhi a std::vector. Eu não quero ter que fazer o loop padrão parapush_back todos os valores individualmente, seria bom se eu pudesse apenas copiar tudo usando algo semelhante a memcpy.

bsruth
fonte

Respostas:

117

Se você puder construir o vetor depois de obter a matriz e o tamanho da matriz, basta dizer:

std::vector<ValueType> vec(a, a + n);

... presumindo que aé o seu array e né o número de elementos que ele contém. Caso contrário, std::copy()w / resize()farei o truque.

Eu ficaria longe, a memcpy()menos que você tenha certeza de que os valores são tipos de dados simples (POD).

Além disso, é importante notar que nada disso realmente evita o loop for - é apenas uma questão de saber se você tem que ver isso em seu código ou não. O desempenho do tempo de execução O (n) é inevitável para copiar os valores.

Finalmente, observe que os arrays de estilo C são recipientes perfeitamente válidos para a maioria dos algoritmos STL - o ponteiro bruto é equivalente a begin(), e ( ptr + n) é equivalente a end().

Drew Hall
fonte
4
O motivo pelo qual o loop e a chamada de push_back são ruins é porque você pode forçar o vetor a ser redimensionado várias vezes se a matriz for longa o suficiente.
bradtgmurray
@bradtgmurray: Acho que qualquer implementação razoável do construtor de vetor "dois iteradores" que sugeri acima chamaria std :: distance () primeiro nos dois iteradores para obter o número necessário de elementos e, em seguida, alocaria apenas uma vez.
Drew Hall
4
@bradtgmurray: Mesmo push_back () não seria tão ruim por causa do crescimento exponencial dos vetores (também conhecido como "tempo constante amortizado"). Acho que o tempo de execução seria apenas da ordem de 2x pior no pior caso.
Drew Hall
2
E se o vetor já estiver lá, um vec.clear (); vec.insert (vec.begin (), a, a + n); funcionaria também. Então, você nem mesmo exigiria que a fosse um ponteiro, apenas um iterador, e a atribuição de vetor seria failry geral (e do modo C ++ / STL).
MP24
6
Outra alternativa quando não é possível construção seria atribuir : vec.assign(a, a+n), o que seria mais compacto do que copiar e redimensionamento.
mMontu
209

Houve muitas respostas aqui e quase todas elas farão o trabalho.

No entanto, existem alguns conselhos enganosos!

Aqui estão as opções:

vector<int> dataVec;

int dataArray[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
unsigned dataArraySize = sizeof(dataArray) / sizeof(int);

// Method 1: Copy the array to the vector using back_inserter.
{
    copy(&dataArray[0], &dataArray[dataArraySize], back_inserter(dataVec));
}

// Method 2: Same as 1 but pre-extend the vector by the size of the array using reserve
{
    dataVec.reserve(dataVec.size() + dataArraySize);
    copy(&dataArray[0], &dataArray[dataArraySize], back_inserter(dataVec));
}

// Method 3: Memcpy
{
    dataVec.resize(dataVec.size() + dataArraySize);
    memcpy(&dataVec[dataVec.size() - dataArraySize], &dataArray[0], dataArraySize * sizeof(int));
}

// Method 4: vector::insert
{
    dataVec.insert(dataVec.end(), &dataArray[0], &dataArray[dataArraySize]);
}

// Method 5: vector + vector
{
    vector<int> dataVec2(&dataArray[0], &dataArray[dataArraySize]);
    dataVec.insert(dataVec.end(), dataVec2.begin(), dataVec2.end());
}

Para encurtar a história, o Método 4, usando vector :: insert, é o melhor para o cenário de bsruth.

Aqui estão alguns detalhes sangrentos:

Método 1 é provavelmente o mais fácil de entender. Basta copiar cada elemento da matriz e colocá-lo na parte de trás do vetor. Infelizmente, é lento. Como há um loop (implícito na função de cópia), cada elemento deve ser tratado individualmente; nenhuma melhoria de desempenho pode ser feita com base no fato de que sabemos que a matriz e os vetores são blocos contíguos.

O Método 2 é uma sugestão de melhoria de desempenho para o Método 1; apenas pré-reserve o tamanho do array antes de adicioná-lo. Para matrizes grandes, isso pode ajudar. No entanto, o melhor conselho aqui é nunca usar reserva, a menos que a criação de perfil sugira que você possa obter uma melhoria (ou você precisa garantir que seus iteradores não sejam invalidados). Bjarne concorda . A propósito, descobri que esse método tinha desempenho mais lento na maioria das vezes, embora eu esteja lutando para explicar de forma abrangente por que era regularmente significativamente mais lento do que o método 1 ...

O método 3 é a solução da velha escola - jogue um pouco de C no problema! Funciona bem e rápido para tipos de POD. Nesse caso, resize deve ser chamado, pois memcpy funciona fora dos limites do vetor e não há como dizer a um vetor que seu tamanho mudou. Além de ser uma solução feia (cópia de bytes!), Lembre-se de que isso pode ser usado para tipos POD . Eu nunca usaria essa solução.

O Método 4 é o melhor caminho a seguir. O significado é claro, é (geralmente) o mais rápido e funciona para qualquer objeto. Não há nenhuma desvantagem em usar este método para esta aplicação.

O Método 5 é um ajuste no Método 4 - copie a matriz em um vetor e, em seguida, anexe-a. Boa opção - geralmente rápido e claro.

Finalmente, você está ciente de que pode usar vetores no lugar de matrizes, certo? Mesmo quando uma função espera matrizes de estilo c, você pode usar vetores:

vector<char> v(50); // Ensure there's enough space
strcpy(&v[0], "prefer vectors to c arrays");

Espero que ajude alguém por aí!

MattyT
fonte
6
Você não pode se referir a "& dataArray [dataArraySize]" com segurança e portabilidade - ele está desreferenciando um ponteiro / iterador passado-o-fim. Em vez disso, você pode dizer dataArray + dataArraySize para obter o ponteiro sem ter que desreferenciá-lo primeiro.
Drew Hall
2
@Drew: sim, pode, pelo menos em C. Fica definido que &exprnão avalia expr, só computa o endereço dele. E um ponteiro de um passado o último elemento é perfeitamente válido, também.
Roland Illig
2
Você já tentou fazer o método 4 com 2? ou seja, reservar o espaço antes de inserir. Parece que, se o tamanho dos dados for grande, várias inserções precisarão de várias realocações. Como sabemos o tamanho a priori, podemos fazer a realocação, antes de inserir.
Jorge Leitao
2
@MattyT qual é o objetivo do método 5? Por que fazer uma cópia intermediária dos dados?
Ruslan
2
Eu pessoalmente prefiro lucrar com arrays se transformando em ponteiros automaticamente: dataVec.insert(dataVec.end(), dataArray, dataArray + dataArraySize);- parece muito mais claro para mim. Também não é possível ganhar nada com o método 5, apenas parece bastante ineficiente - a menos que o compilador seja capaz de otimizar o vetor novamente.
Aconcágua
37

Se tudo o que você está fazendo é substituir os dados existentes, você pode fazer isso

std::vector<int> data; // evil global :)

void CopyData(int *newData, size_t count)
{
   data.assign(newData, newData + count);
}
Torlack
fonte
1
Simples de entender e definitivamente a solução mais rápida (é apenas um memcpy nos bastidores).
Don Scott
Deta.assign é mais rápido do que data.insert?
Jim
11

std :: copy é o que você está procurando.

Lucas
fonte
10

Como só posso editar minha própria resposta, farei uma resposta composta com as outras respostas à minha pergunta. Obrigado a todos vocês que responderam.

Usando std :: copy , isso ainda itera em segundo plano, mas você não precisa digitar o código.

int foo(int* data, int size)
{
   static std::vector<int> my_data; //normally a class variable
   std::copy(data, data + size, std::back_inserter(my_data));
   return 0;
}

Usando memcpy regular . Provavelmente, é melhor usado para tipos de dados básicos (ou seja, int), mas não para matrizes mais complexas de estruturas ou classes.

vector<int> x(size);
memcpy(&x[0], source, size*sizeof(int));
bsruth
fonte
Eu recomendaria essa abordagem.
mmocny
É provavelmente mais eficiente redimensionar seu vetor antecipadamente se você souber o tamanho com antecedência e não usar o back_inserter.
luke
você pode adicionar my_data.reserve (size)
David Nehme
Observe que, internamente, isso está fazendo exatamente o que você parece querer evitar. Não está copiando bits, está apenas fazendo um loop e chamando push_back (). Eu acho que você só queria evitar digitar o código.
mmocny
1
Você não usa o construtor vetorial para copiar os dados?
Martin York
3

evite o memcpy, eu digo. Não há razão para mexer com as operações de ponteiro, a menos que seja realmente necessário. Além disso, só funcionará para tipos de POD (como int), mas falhará se você estiver lidando com tipos que requerem construção.

Assaf Lavie
fonte
8
Talvez isso deva ser um comentário sobre uma das outras respostas, já que você não está propondo uma solução.
finnw 01 de
3
int dataArray[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//source

unsigned dataArraySize = sizeof(dataArray) / sizeof(int);

std::vector<int> myvector (dataArraySize );//target

std::copy ( myints, myints+dataArraySize , myvector.begin() );

//myvector now has 1,2,3,...10 :-)
Antonio Ramasco
fonte
2
Embora este trecho de código seja bem-vindo e possa fornecer alguma ajuda, seria muito melhor se incluísse uma explicação de como e por que isso resolve o problema. Lembre-se de que você está respondendo às perguntas dos leitores no futuro, não apenas da pessoa que está perguntando agora! Por favor edite sua resposta para adicionar explicação, e dar uma indicação do que limitações e premissas se aplicam.
Toby Speight
4
Espere, o que é myints?
mavavilj
2

Ainda outra resposta, uma vez que a pessoa disse "Não sei quantas vezes minha função será chamada", você poderia usar o método de inserção de vetor para anexar matrizes de valores ao final do vetor:

vector<int> x;

void AddValues(int* values, size_t size)
{
   x.insert(x.end(), values, values+size);
}

Gosto dessa forma porque a implementação do vetor deve ser capaz de otimizar a melhor maneira de inserir os valores com base no tipo de iterador e no próprio tipo. Você está respondendo um pouco sobre a implementação de stl.

Se você precisa garantir a velocidade mais rápida e sabe que seu tipo é um tipo POD, eu recomendaria o método de redimensionamento na resposta de Thomas:

vector<int> x;

void AddValues(int* values, size_t size)
{
   size_t old_size(x.size());
   x.resize(old_size + size, 0);
   memcpy(&x[old_size], values, size * sizeof(int));
}
Shane Powell
fonte
1

Além dos métodos apresentados acima, você precisa ter certeza de usar std :: Vector.reserve (), std :: Vector.resize (), ou construir o vetor para dimensionar, para garantir que seu vetor tenha elementos suficientes em para armazenar seus dados. do contrário, você corromperá a memória. Isso é verdade para std :: copy () ou memcpy ().

Esta é a razão de usar vector.push_back (), você não pode escrever além do final do vetor.

Thomas Jones-Low
fonte
Se você estiver usando um back_inserter, não precisa pré-reservar o tamanho do vetor para o qual está copiando. back_inserter faz um push_back ().
John Dibling
0

Supondo que você saiba o tamanho do item no vetor:

std::vector<int> myArray;
myArray.resize (item_count, 0);
memcpy (&myArray.front(), source, item_count * sizeof(int));

http://www.cppreference.com/wiki/stl/vector/start

Thomas Jones-Low
fonte
Isso não depende da implementação de std :: vector?
ReaperUnreal
Isso é horrível! Você está preenchendo o array duas vezes, um com '0's, e então com os valores apropriados. Basta fazer: std :: vector <int> myArray (source, source + item_count); e confie no seu compilador para produzir o memcpy!
Chris Jefferson
Confie em seu compilador para produzir __memcpy_int_aligned; isso deve ser ainda mais rápido
MSalters