Pergunta

Se um C ++ (implícita ou explícita) construtor valor aceitar seu parâmetro (s) por valor ou referência-a-const, quando ele precisa para armazenar uma cópia do argumento (s) em seu objeto de qualquer forma?

Aqui está o exemplo mais curta que eu posso pensar de:

struct foo {
    bar _b;
    foo(bar [const&] b) // pass by value or reference-to-const?
        : _b(b) { }
};

A idéia aqui é que eu quero para minimizar as chamadas para o bar do construtor copiar quando um objeto foo é criado, em qualquer uma das várias maneiras em que um objeto foo pode ter criado.

Por favor note que eu sei um pouco sobre cópia elisão e (Nomeado) Valor de retorno Optimization, e eu li 'Quer velocidade? passagem por valor' , no entanto eu não acho que o artigo aborda diretamente este caso de uso.

Editar:. eu deveria ser mais específico

Suponha que eu não posso saber o sizeof(bar), ou se ou não bar é um princípio essencial, built-in type (bar pode ser um parâmetro de modelo, e foo pode ser um modelo de classe em vez de uma classe). Além disso, não assuma construtor que de foo pode ser embutido (ou bar de, para que o assunto). Não supor que eu, pelo menos, pode estar usando um compilador que implementos OVCR.

O que eu gostaria é para que haja uma possibilidade (dadas otimizações do compilador) que uma chamada como este irá invocar há chamadas para construtor de cópia de bar qualquer (mesmo quando execução _b(b) na lista de inicialização do foo):

foo f = function_that_creates_and_returns_a_bar_object_using_rvo();

Existe alguma possibilidade (dado o C ++ 98 standard) que isso pode ser feito, e se assim for, é mais ou menos provável de trabalho, se foo aceita seu parâmetro de referência-a-const em vez de por valor?

Foi útil?

Solução

Em C ++ 98 e C ++ 03, você deve passar const& bar e copie. Em C ++ 0x, você deve passar bar e depois fazer um movimento (desde bar tem um construtor movimento).

#include <utility>

struct foo
{
    bar _b;

    foo(bar b) : _b(std::move(b)) {}
};

Se você construir foo com um parâmetro lvalue, o construtor de cópia será chamado para criar uma b cópia, e essa cópia será movido para _b. Se você construir foo com um parâmetro de rvalue, construtor movimento de bar será chamado a se mudar para b, e, em seguida, ele será movido novamente em _b.

Outras dicas

Todas as coisas são iguais, eu passar por const & para classes suficientemente complexas, eo valor para o POD e objetos simples.

Deitado as vantagens / desvantagens de passar por referência const em vez de passagem tradicional pelo valor

Positivos:

  • Evita uma cópia (grande vantagem para objetos com uma cópia caro)
  • ACCESS read-only

Negativos:

  • Alguém pode Const lançar o const fora da referência, se eles realmente queriam

Mais importante com os pontos positivos é que você explicitamente controle quando a cópia acontece (no seu caso ao passar inicializar _B em seu inicializador lista). Pensando sobre os negativos ... Concordo que é um risco. Eu acho que quase todos os programadores bons vai se sentir sujo sobre emplying o const_cast. Além disso você pode obedientemente procurar const_cast e jogue os tomates para a pessoa lançando o const fora do argumento. Mas hey, você nunca sabe e quem tem tempo para assistir a um código como um falcão:?)

Meu opinião subjetiva é que nas classes suficientemente complexos e em ambientes onde o desempenho importa o benefício de evitar o construtor de cópia supera o risco. No entanto, para as classes realmente mudos e POD, eu tendem a fazer cópias dos dados e passar por valor.

Olhe este questão .

Use const T & arg se sizeof(T)>sizeof(void*) e uso T arg se sizeof(T) <= sizeof(void*). Todos os tipos fundamentais deve ser a exceção a esta regra

Estilisticamente, eu diria que passar por referência é o melhor caminho.

Se o desempenho realmente importa, então não acho. Medi-lo.

Eu não tenho verificado que o padrão diz, mas tentando este empiricamente posso dizer-lhe que o GCC não otimizar a cópia de distância, independentemente do construtor aceita o argumento por valor ou por referência const.

Se, no entanto, o construtor leva uma referência const, consegue evitar uma cópia desnecessário ao criar foo a partir de um já existente bar objeto.

Para resumir:

Bar b = makeBar();         // No copy, because of RVO
FooByValue f1 = makeBar(); // Copy constructor of Bar called once
FooByRef f2 = makeBar();   // Copy constructor of Bar called once
FooByValue f3(b);          // Copy constructor of Bar called twice
FooByRef f4(b);            // Copy constructor of Bar called only once

Não é que eu sou um especialista compilador, mas eu acho que faz sentido que geralmente não podem OVCR um valor de retorno em um lugar arbitrário (como o campo de membro do foo objeto). Em vez disso, ele requer o alvo para estar no topo da pilha.

Estou assumindo bar tem um construtor de cópia normal do formulário bar(const bar &b)

Tome uma referência const aqui. construtor de bar, então, tomar essa referência, e executar a cópia. Total de cópias:. 1

Se você deixou a referência off, o compilador iria fazer uma cópia de b, passar a cópia para o construtor de foo, o que, em seguida, passá-lo para bar e copiar isso.

Honestamente, para este caso, a melhor opção é fazer a sua inline construtor, construtor fornecida bar não joga. Em seguida, basta passar por referência.

Além disso, como você suspeita, o seu artigo não se aplica aqui.

Mantenha um shared_pointer para barrar em sua classe e passá-lo como tal. Dessa forma, você nunca chama o construtor de cópia:.)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top