Estou criando uma classe do tipo encadeamento, como o pequeno exemplo abaixo. Parece que, ao encadear funções-membro, o construtor de cópia é chamado. Existe uma maneira de se livrar da chamada do construtor de cópia? No meu exemplo de brinquedo abaixo, é óbvio que estou lidando apenas com temporários e, portanto, "deveria" (talvez não pelos padrões, mas logicamente) haver uma elisão. A segunda melhor opção, copiar elision, seria chamar o construtor de movimento, mas esse não é o caso.
class test_class {
private:
int i = 5;
public:
test_class(int i) : i(i) {}
test_class(const test_class& t) {
i = t.i;
std::cout << "Copy constructor"<< std::endl;
}
test_class(test_class&& t) {
i = t.i;
std::cout << "Move constructor"<< std::endl;
}
auto& increment(){
i++;
return *this;
}
};
int main()
{
//test_class a{7};
//does not call copy constructor
auto b = test_class{7};
//calls copy constructor
auto b2 = test_class{7}.increment();
return 0;
}
Edit: Alguns esclarecimentos. 1. Isso não depende do nível de otimização. 2. No meu código real, tenho objetos mais complexos (por exemplo, alocados por heap) do que ints
auto b = test_class{7};
não chama construtor de cópia porque é realmente equivalentetest_class b{7};
e os compiladores são inteligentes o suficiente para reconhecer esse caso e, portanto, podem facilmente excluir qualquer cópia. O mesmo não pode ser feitob2
.std::cout
) no seu copiador? Sem ele, a cópia deve ser otimizada.Respostas:
Resposta parcial (ela não é construída
b2
no lugar, mas transforma a construção da cópia em uma construção de movimentação): Você pode sobrecarregar aincrement
função de membro na categoria de valor da instância associada:Isso causa
mover-construir
b2
porquetest_class{7}
é temporário e a&&
sobrecarga detest_class::increment
é chamada.Para uma verdadeira construção no local (ou seja, nem mesmo uma construção de movimentação), você pode transformar todas as funções de membros especiais e não especiais em
constexpr
versões. Então você pode fazere você não precisa mover nem construir uma cópia para pagar. Obviamente, isso é possível para o
test_class
cenário simples , mas não para um cenário mais geral que não permitaconstexpr
funções de membro.fonte
b2
não modificável.constinit
que seria o caminho a percorrer lá.this
é exatamente o mesmo queconst
as funções de membroBasicamente, atribuir uma referência a um valor requer a invocação de um construtor, ou seja, uma cópia ou uma movimentação . Isso é diferente da cópia elision, onde é conhecido nos dois lados da função o mesmo objeto distinto. Também uma referência pode se referir a um objeto compartilhado como um ponteiro.
A maneira mais simples é provavelmente fazer com que o construtor de cópias seja totalmente otimizado. A configuração do valor já é otimizada pelo compilador, é exatamente
std::cout
isso que não pode ser otimizado.(ou apenas remova a cópia e mova o construtor)
exemplo ao vivo
Como seu problema é basicamente com a referência, uma solução provavelmente não está retornando uma referência ao objeto se você deseja parar de copiar dessa maneira.
Um terceiro método é apenas confiar na remoção de cópias - no entanto, isso requer uma reescrita ou encapsulamento da operação em uma função e, portanto, evitando o problema completamente (sei que isso pode não ser o que você deseja, mas pode ser um solução para outros usuários):
Um quarto método está usando uma movimentação, invocada explicitamente
ou como visto nesta resposta .
fonte