No C ++ tradicional, a passagem de valor para funções e métodos é lenta para objetos grandes e geralmente é desaprovada. Em vez disso, os programadores de C ++ tendem a passar referências, o que é mais rápido, mas apresenta todos os tipos de perguntas complicadas sobre propriedade e principalmente sobre gerenciamento de memória (no caso de o objeto ser alocado por heap)
Agora, no C ++ 11, temos referências Rvalue e movemos construtores, o que significa que é possível implementar um objeto grande (como um std::vector
) que é barato para passar valor por uma função.
Então, isso significa que o padrão deve ser passar valor para instâncias de tipos como std::vector
e std::string
? E os objetos personalizados? Qual é a nova melhor prática?
c++
coding-style
c++11
Derek Thurn
fonte
fonte
pass by reference ... which introduces all sorts of complicated questions around ownership and especially around memory management (in the event that the object is heap-allocated)
. Não entendo como é complicado ou problemático para a propriedade? Pode ser que eu perdi alguma coisa?const std::string&
e não uma cópia. O primeiro thread então saiu ...Respostas:
É um padrão razoável se você precisar fazer uma cópia dentro do corpo. Isto é o que Dave Abrahams está defendendo :
No código, isso significa não fazer isso:
mas faça o seguinte:
qual tem a vantagem que o chamador pode usar
foo
assim:e apenas um trabalho mínimo é feito. Você precisaria de duas sobrecargas para fazer o mesmo com referências
void foo(T const&);
evoid foo(T&&);
.Com isso em mente, agora escrevi meus valiosos construtores como tais:
Caso contrário, passar por referência a
const
still é razoável.fonte
SomeProperty p;
for (auto x: vec) { x.foo(p); }
não cabe, por exemplo. Além disso, os Mover Construtores têm um custo (quanto maior o objeto, maior o custo), enquantoconst&
são essencialmente gratuitos.std::vector
com um milhão de elementos custa o mesmo que mover um com cinco elementos, pois apenas o ponteiro para a matriz na pilha é movido, nem todos os objetos no vetor. Portanto, não é realmente um problema tão grande.std::move
todo o lugar ..const&
que me tropeçou algumas vezes.void foo(const T&); int main() { S s; foo(s); }
. Isso pode ser compilado, mesmo que os tipos sejam diferentes, se houver um construtor T que use S como argumento. Isso pode ser lento, porque um objeto T grande pode estar sendo construído. Você pode pensar em passar uma referência sem copiar, mas pode estar. Veja esta resposta a uma pergunta que pedi mais. Basicamente,&
geralmente se liga apenas a lvalues, mas há uma exceção pararvalue
. Existem alternativas.Em quase todos os casos, sua semântica deve ser:
Todas as outras assinaturas devem ser usadas apenas com moderação e com boa justificativa. O compilador agora praticamente sempre trabalha isso da maneira mais eficiente. Você pode simplesmente escrever seu código!
fonte
foo(bar& x) { x.a = 3; }
É um pedaço de um monte mais confiável (e legível!) Do quefoo(bar* x) {if (!x) throw std::invalid_argument("x"); x->a = 3;
ref
palavra - chave em C #).is shared_ptr intended to never be null? Much as (I think) unique_ptr is?
Ambas as suposições estão incorretas.unique_ptr
eshared_ptr
pode conternullptr
valores / nulos . Se você não quiser se preocupar com valores nulos, deve usar referências, porque elas nunca podem ser nulas. Você também não terá que digitar->
, o que você acha que seja irritante :)Passe parâmetros por valor se, dentro do corpo da função, você precisar de uma cópia do objeto ou apenas precisar movê-lo. Passe
const&
se você precisar apenas de acesso sem mutação ao objeto.Exemplo de cópia de objeto:
Exemplo de movimentação de objeto:
Exemplo de acesso sem mutação:
Para justificativa, consulte as postagens de Dave Abrahams e Xiang Fan .
fonte
A assinatura de uma função deve refletir seu uso pretendido. A legibilidade é importante, também para o otimizador.
Essa é a melhor pré-condição para um otimizador criar código mais rápido - pelo menos em teoria e, se não na realidade, em alguns anos.
As considerações de desempenho são muitas vezes superestimadas no contexto da passagem de parâmetros. O encaminhamento perfeito é um exemplo. Funções como a
emplace_back
maioria são muito curtas e alinhadas de qualquer maneira.fonte