#include <iostream>
#include <vector>
class Car{
public:
int weight;
Car(int weight): weight(weight){
};
Car(const Car& other){
std::cout<<"copied!"<<std::endl;
this->weight=other.weight;
}
};
int main() {
std::vector<Car> vec;
vec.push_back(Car(1));
return 0;
}
No código acima, de alguma forma, quando eu empurro Car(1) para vec, o construtor de cópia na classe Car é chamado. Por que o construtor de cópia é chamado? Não estou copiando nada aqui.
Você está copiando, pois sua
Car
struct não implementa semântica de movimento. A cópia é do temporário com o qual você construiuCar(1)
para o objeto criado no vetor. Usaremplace_back
para construir diretamente o objeto no vetor sem o objeto temporário evitará isso.Observe que, mesmo usando,
emplace_back
você ainda poderá ver construtores de cópia chamados se o vetor tiver que ser realocado.Um pouco de elaboração sobre o tópico devido ao fato de você ter rotulado sua pergunta com
move-semantics
tag. Minha suposição é que você esperava que seu código expusesse essa semântica aqui e, portanto, sua surpresa aqui.Como você provavelmente sabe, no C++ 11 todos os contêineres foram atualizados com suporte para mover objetos para seu armazenamento em vez de copiá-los. Agora, para a maioria das funções projetadas para inserir um novo valor em um contêiner, temos algo como:
Qual destes é selecionado depende do tipo de parâmetro e da habilidade de T de ser copy- e/ou move-constructed . E é aqui que seu código erra.
De fato,
vec.push_back(Car(1));
deve mover-inserir o valor para o contêiner (Car(1)
é um limite temporário para um rvalue, entãopush_back( T&& value );
é definitivamente a sobrecarga correspondente). O motivo pelo qual isso não está acontecendo é porque sua classe não fornece semântica de movimentação.Por padrão, o compilador gera todo o conjunto de construtores, operadores de atribuição e um destrutor (depende dos membros armazenados dentro de uma classe e suas habilidades de copiar/mover), mas quando versões definidas pelo usuário aparecem, esse não é mais o caso .
O texto que você deve procurar pode ser encontrado aqui, por exemplo , e é o seguinte:
E aí está - já que você definiu seu próprio construtor de cópia, o primeiro requisito não foi atendido , então você precisa escrever seu próprio construtor de movimento se quiser (e você definitivamente deve fazer o mesmo para operadores de atribuição relevantes também).
Se você não conhece os termos "Regra dos Três (Cinco desde C++ 11)", dê uma olhada aqui .
Depois de elaborar esse problema simples e bastante longo, vamos finalmente corrigir seu código:
Podemos então executar seu código e observar o resultado correto que você esperava:
Um `Car(1)` temporário está sendo empurrado para o vetor, o construtor de cópia é chamado. C++ adiciona o item ao vetor usando o construtor de cópia porque sua classe não tem um construtor de movimento. O vetor deve criar uma cópia para armazená-lo, mesmo que pareça que você não está copiando.