Pergunta

eu estava lendo Copie e trocar.

Tentei ler alguns links sobre copiar a elisão, mas não consegui descobrir corretamente o que isso significava. Alguém pode explicar o que é essa otimização, e especialmente o que é mau pelo texto a seguir

Isso não é apenas uma questão de conveniência, mas de fato uma otimização. Se o (s) parâmetro (s) se ligar a um LValue (outro objeto que não consiga), uma cópia do objeto será feita automaticamente ao criar o (s) parâmetro (s). No entanto, quando S se liga a um Rvalue (objeto temporário, literal), a cópia é tipicamente Elided, o que salva uma chamada para um construtor de cópias e um destruidor. Na versão anterior do operador de atribuição, onde o parâmetro é aceito como referência const, a cópia da elisão não acontece quando a referência se liga a um RValue. Isso resulta em um objeto adicional que está sendo criado e destruído.

Foi útil?

Solução

O construtor de cópias existe para fazer cópias. Em teoria quando você escreve uma linha como:

CLASS c(foo());

O compilador teria que ligar para o construtor de cópias para copiar o retorno de foo() em c.

A elisão de copiar é uma técnica para pular chamando o construtor de cópias para não pagar pela sobrecarga.

Por exemplo, o compilador pode organizar isso foo() construirá diretamente seu valor de retorno em c.

Aqui está outro exemplo. Digamos que você tenha uma função:

void doit(CLASS c);

Se você chamá -lo com um argumento real, o compilador deve invocar o construtor de cópias para que o parâmetro original não possa ser modificado:

CLASS c1;
doit(c1);

Mas agora considere um exemplo diferente, digamos que você chame sua função assim:

doit(c1 + c1);

operator+ vai ter que criar um objeto temporário (um rvalue). Em vez de invocar o construtor de cópias antes de ligar doit(), o compilador pode passar o temporário que foi criado por operator+ e passar isso para doit() em vez de.

Outras dicas

Aqui está um exemplo:

#include <vector>
#include <climits>

class BigCounter {
 public:
   BigCounter &operator =(BigCounter b) {
      swap(b);
      return *this;
   }

   BigCounter next() const;

   void swap(BigCounter &b) {
      vals_.swap(b);
   }

 private:
   typedef ::std::vector<unsigned int> valvec_t;
   valvec_t vals_;
};

BigCounter BigCounter::next() const
{
   BigCounter newcounter(*this);
   unsigned int carry = 1;
   for (valvec_t::iterator i = newcounter.vals_.begin();
        carry > 0 && i != newcounter.vals_.end();
        ++i)
   {
      if (*i <= (UINT_MAX - carry)) {
         *i += carry;
      } else {
         *i += carry;
         carry = 1;
      }
   }
   if (carry > 0) {
      newcounter.vals_.push_back(carry);
   }
   return newcounter;
}

void someFunction()
{
    BigCounter loopcount;
    while (true) {
       loopcount = loopcount.next();
    }
}

Dentro somefunction a linha loopcount = loopcount.next(); beneficia muito da copiar a elisão. Se a cópia não fosse permitida, essa linha exigiria três invocações do construtor de cópias e uma chamada associada a um destruidor. Com a cópia da elisão sendo permitida, pode ser reduzida para 1 chamada do construtor de cópias, o explícito dentro de BigCount::next() Onde newcounter é declarado.

Se operator = foi declarado e definido assim:

BigCounter &BigCounter::operator =(const BigCounter &b) {
   BigCounter tmp(b);
   swap(tmp);
   return *this;
}

Houve que houve 2 invocações do construtor de cópias, mesmo com a cópia Elision. Um para construir newcounter e o outro para construir tmp. E sem copiar a elisão, ainda haveria 3. é por isso que declarar operator = Portanto, seu argumento requer invocar a construção de cópia pode ser uma otimização ao usar o idioma 'cópia e troca' para o operador de atribuição. Quando o construtor de cópias é chamado para a construção de um argumento, sua invocação pode ser eliminada, mas se for invocada para criar uma variável local, pode não ser.

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