Pergunta

class A
{
 public:
  A(const int n_);
  A(const A& that_);
  A& operator=(const A& that_);
};

A::A(const int n_)
{ cout << "A::A(int), n_=" << n_ << endl; }

A::A(const A& that_)    // This is line 21
{ cout << "A::A(const A&)" << endl; }

A& A::operator=(const A& that_)
{ cout << "A::operator=(const A&)" << endl; }

int foo(const A& a_)
{ return 20; }

int main()
{
  A a(foo(A(10)));    // This is line 38
  return 0;
}

Ao executar este código dá o / p:

A :: A (int), n_ = 10
A :: A (int), n_ = 20

Aparentemente, o construtor de cópia nunca é chamado.

class A
{
 public:
  A(const int n_);
  A& operator=(const A& that_);
 private:
  A(const A& that_);
};

No entanto, se nós torná-lo privado, ocorre este erro de compilação:

Test.cpp: Em função ‘int main ()’:
Test.cpp: 21: erro: ‘A :: A (const A &)’ é
privada Test.cpp: 38: erro: neste contexto

Por que o compilador reclamar quando ele realmente não usar o construtor de cópia?
Eu estou usando gcc versão 4.1.2 20.070.925 (Red Hat 4.1.2-33)

Foi útil?

Solução

Núcleo desertar 391 explica o problema.

Basicamente, o padrão actual C ++ requer um construtor de cópia para estar disponível quando passar um temporária do tipo de classe para uma referência constante.

Esta exigência será removido em C ++ 0x.

A lógica por trás exigindo um construtor de cópia vem deste caso:

C f();
const C& r = f(); // a copy is generated for r to refer to

Outras dicas

O padrão de 2003, no §12.2 / 1, estados:

Mesmo quando a criação do objeto temporário é evitada (12,8), todas as restrições semânticas deve ser respeitados como se o objeto temporário foi criado. [Exemplo: mesmo se o construtor de cópia não é chamado, todos as restrições semânticas, tais como acessibilidade (cláusula 11), será satisfeito. ]

Há exemplos semelhantes ao redor. Pelo que pude perceber, o compilador é livre para gerar temporários ou otimizar-los.

Como agora eu vejo que você não está usando a qualquer construtor de cópia. No foo(A(10)) declaração que você está criando um objeto temporário da classe A e passá-la como uma const referência para foo. O foo retorna um número inteiro que é usado na construção de objecto a. Daí eu não vejo onde o construtor de cópia é se envolver aqui e como NRVO entra em cena. Além disso, eu compilado o código a seguir, fazendo o construtor de cópia privada e compilado muito bem no VS2008.

using namespace std;

class A
{
 public:
  A(const int n_);
 private:
  A(const A& that_);
  A& operator=(const A& that_);
};

A::A(const int n_)
{ cout << "A::A(int), n_=" << n_ << endl; }

A::A(const A& that_)    // This is line 21
{ cout << "A::A(const A&)" << endl; }

A& A::operator=(const A& that_)
{ 
    cout << "A::operator=(const A&)" << endl; 
    return *this;
}

int foo(const A& a_)
{ return 20; }


int main(int argc,char *argv[])
{
   A a(foo(A(10)));    // This is line 38
  return 0;

}   

Apenas uma outra observação: o compilador faz uma coisa diferente quando se trabalha com um temporário. Portanto, não é sobre o construtor de cópia, é sobre o intermediário temporário.

A original(10);
foo( original ); // does compile
foo( A(10) ); // doesn't compile - needs a copy constructor

Na expressão:

A a(foo(A(10)));

O resultado da A(10) sub-expressão é uma rvalue do tipo A. (5.2.3 [expr.type.conv])

Quando inicializar uma referência constante a partir de uma rvalue o compilador pode criar um temporária do rvalue e ligam-se que para a referência. Mesmo que escolhe não, o construtor de cópia deve ser acessível. (8.5.3 [decl.init.ref]) Este não seria o caso se houver referência estavam sendo inicializado a partir de um referência compatível lvalue , onde a ligação direta é obrigatória.

Como foo leva seu parâmetro por referência e não o valor, não há cópia mandatado para o argumento de inicialização em si.

foo retorna um int, então não há nenhuma cópia de um A aqui.

a é direta inicializada a partir do int retornado por foo, então não há nenhuma cópia do A aqui.

A cópia-construtor não é usado, mas para que o código para compilar a necessidade de copiar construtor para ser acessível.

EDIT: Comeau compilador C ++ relata o seguinte:

Comeau C/C++ 4.3.10.1 (Oct  6 2008 11:28:09) for ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing.  All rights reserved.
MODE:strict errors C++ noC++0x_extensions

"ComeauTest.c", line 38: error: "A::A(const A &)" (declared at line 17), required
          for copy that was eliminated, is inaccessible
    A a(foo(A(10)));    // This is line 38
            ^

1 error detected in the compilation of "ComeauTest.c".

Note que, se C ++ 0x extensões são ativadas, ele compila bem no compilador Comeau C ++.

Em geral, você não deve chegar preocupado com se e quando o construtor de cópia é chamado. O padrão C ++ é muito relaxado sobre quando chamadas para o construtor de cópia serão removidos, ou para essa matéria acrescentou. Se sua classe logicamente precisa dele, fornecê-la (e não se esqueça o operador destructor e atribuição) é a regra sensata.

Ao chamar:

foo( A(10) );

um objeto temporário está sendo criado durante o tempo de vida da chamada. Um construtor de cópia está sendo sendo usado para preencher os dados. O objeto temporário é removido após a execução da chamada.

Ao chamar:

{ 
  A original(10);
  foo( original ); 
}

O original está sendo descartado depois de sair do bloco. Ele pode ser usado com segurança como um parâmetro.

Para a velocidade óptima, passar o objecto por referência, utilizando uma variável temporária que serão descartados pelo compilador durante a sua optimização.

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