Pergunta

Aqui está um extrato do item 56 do livro "C ++ Gotchas":

Não é incomum ver uma simples inicialização de um objeto Y escrito de qualquer uma das três maneiras diferentes, como se fossem equivalentes.

Y a( 1066 ); 
Y b = Y(1066);
Y c = 1066;

De fato, todas essas três inicializações provavelmente resultarão no mesmo código de objeto que está sendo gerado, mas não são equivalentes. A inicialização de A é conhecida como inicialização direta e faz com precisão o que se poderia esperar. A inicialização é realizada através de uma invocação direta de y :: y (int).

As inicializações de B e C são mais complexas. Na verdade, eles são complexos demais. Estas são as duas inicializações de cópia. No caso da inicialização de B, estamos solicitando a criação de um temporário anônimo do tipo Y, inicializado com o valor 1066. Em seguida, usamos esse temporário anônimo como um parâmetro para o construtor de cópias para a classe Y para inicializar b. Finalmente, chamamos o destruidor do anônimo temporário.

Para testar isso, fiz uma classe simples com um membro de dados (programa anexado no final) e os resultados foram surpreendentes. Parece que, para o caso de C, o objeto foi construído pelo construtor de cópias e não como sugerido no livro.

Alguém sabe se o padrão de idioma mudou ou isso é simplesmente um recurso de otimização do compilador? Eu estava usando o Visual Studio 2008.

Exemplo de código:

#include <iostream>

class Widget
{
    std::string name;
public:
    // Constructor
    Widget(std::string n) { name=n; std::cout << "Constructing Widget " << this->name << std::endl; }
    // Copy constructor
    Widget (const Widget& rhs) { std::cout << "Copy constructing Widget from " << rhs.name << std::endl; }
    // Assignment operator
    Widget& operator=(const Widget& rhs) { std::cout << "Assigning Widget from " << rhs.name << " to " << this->name << std::endl; return *this; }
};

int main(void)
{
    // construct
    Widget a("a");
    // copy construct
    Widget b(a);
    // construct and assign
    Widget c("c"); 
    c = a;
    // copy construct!
    Widget d = a;
    // construct!
    Widget e = "e";
    // construct and assign
    Widget f = Widget("f");

    return 0;
}

Resultado:

Constructing Widget a

Copy constructing Widget from a

Constructing Widget c
Assigning Widget from a to c

Copy constructing Widget from a

Constructing Widget e

Constructing Widget f
Copy constructing Widget from f

Fiquei muito surpreso com os resultados da construção de d e e. Para ser mais preciso, eu esperava que um objeto vazio fosse criado e, em seguida, um objeto a ser criado e atribuído ao objeto vazio. Na prática, os objetos foram criados pelo construtor de cópias.

Foi útil?

Solução

A sintaxe

X a = b;

onde A e B são do tipo X sempre significava construção de cópias. Quaisquer variantes, como:

X a = X();

são usados, não há nenhuma tarefa acontecendo e nunca foi. Construir e atribuir seria algo como:

X a;
a = X();

Outras dicas

O compilador tem permissão para otimizar casos b e c para ser o mesmo que a. Além disso, as chamadas do operador de construção e tarefa de cópia podem ser totalmente eliminadas pelo compilador, então o que você vê não é necessariamente o mesmo com diferentes compiladores ou mesmo configurações de compilador.

A partir de C ++ 17, todos os três são equivalente (a menos que Y::Y(int) é explicit, o que simplesmente não permitiria c) por causa do que costuma ser chamado Cópia obrigatória Elision.

Até Y c = 1066; cria apenas um Y objeto porque o resultado da conversão implícita para Y é um Prvvalue usado para inicializar c em vez de criar um temporário.

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