Ela costumava ser geralmente recomendado melhores práticas 1 para passar uso por ref const para todos os tipos , exceto para builtin tipos ( char
, int
, double
, etc.), para iterators e para objetos de função (lambdas, classes decorrentes std::*_function
).
Isso era especialmente verdade antes da existência da semântica de movimentação . O motivo é simples: se você passou por valor, era necessário fazer uma cópia do objeto e, exceto para objetos muito pequenos, isso sempre é mais caro do que passar uma referência.
Com o C ++ 11, ganhamos a semântica de movimentos . Em poucas palavras, a semântica de movimentação permite que, em alguns casos, um objeto possa ser passado "por valor" sem copiá-lo. Em particular, esse é o caso quando o objeto que você está passando é um rvalue .
Por si só, mover um objeto ainda é pelo menos tão caro quanto passar por referência. No entanto, em muitos casos, uma função copiará um objeto internamente de qualquer maneira - ou seja, ela assumirá a propriedade do argumento. 2
Nessas situações, temos o seguinte compromisso (simplificado):
- Podemos passar o objeto por referência e depois copiar internamente.
- Podemos passar o objeto por valor.
"Passar por valor" ainda faz com que o objeto seja copiado, a menos que o objeto seja um rvalor. No caso de um rvalue, o objeto pode ser movido, de modo que o segundo caso não é mais "copiar e depois mover", mas "mover e depois (potencialmente) mover novamente".
Para objetos grandes que implementam construtores de movimento adequados (como vetores, strings ...), o segundo caso é muito mais eficiente que o primeiro. Portanto, é recomendável usar a passagem por valor se a função se apropriar do argumento e se o tipo de objeto oferecer suporte à movimentação eficiente .
Uma nota histórica:
De fato, qualquer compilador moderno deve ser capaz de descobrir quando passar valor é caro e converter implicitamente a chamada para usar uma const ref, se possível.
Em teoria. Na prática, os compiladores nem sempre podem mudar isso sem interromper a interface binária da função. Em alguns casos especiais (quando a função está embutida), a cópia será realmente elidida se o compilador puder descobrir que o objeto original não será alterado através das ações na função.
Mas, em geral, o compilador não pode determinar isso, e o advento da semântica de movimentação no C ++ tornou essa otimização muito menos relevante.
1 Por exemplo, em Scott Meyers, C ++ eficaz .
2 Isso é especialmente verdadeiro para construtores de objetos, que podem receber argumentos e armazená-los internamente para fazer parte do estado do objeto construído.