Pergunta

Aqui está um exemplo de código mínimo que ilustra o problema:

#include <iostream>

class Thing
{
   // Non-copyable
   Thing(const Thing&);
   Thing& operator=(const Thing&);

   int n_;

public:
   Thing(int n) : n_(n) {}

   int getValue() const { return n_;}
};

void show(const Thing& t)
{
   std::cout << t.getValue() << std::endl;
}

int main()
{
   show(3);
}

Isso produz o mesmo erro:

int main()
{
    show( Thing(3) );
}

IBM XL C/C ++ 8.0 Compilador sob AIX emite esses avisos:

"testWarning.cpp", line 24.9: 1540-0306 (W) The "private" copy constructor "Thing(const Thing &)" cannot be accessed.
"testWarning.cpp", line 24.9: 1540-0308 (I) The semantics specify that a temporary object must be constructed.
"testWarning.cpp", line 24.9: 1540-0309 (I) The temporary is not constructed, but the copy constructor must be accessible.

Eu também tentei G ++ 4.1.2 com "-wall" e "-Pedantic" e não tenho diagnóstico. Por que o acesso ao construtor de cópias é necessário aqui? Como posso eliminar o aviso, além de fazer com que o objeto seja copiado (que está fora do meu controle) ou fazer uma cópia explícita (quando o objeto da vida real é caro de copiar)?

Foi útil?

Solução

As regras para isso estão em §8.5.3/5 do padrão. Existem três situações básicas identificadas. O primeiro envolve o inicializador ('3' no seu caso) sendo um LValue ou com o tipo de classe. Como nenhum deles é verdadeiro, o que você tem é o terceiro caso: inicializar uma referência const com um RValue que não possui um tipo de classe. Este caso é coberto pela bala final em 8.5.3/5:

Caso contrário, um temporário do tipo “CV1 T1” é criado e inicializado a partir da expressão inicializadora usando as regras para uma inicialização de cópia de não referência (8.5). A referência é então vinculada ao temporário. Se T1 estiver relacionado a referência a T2, o CV1 deverá ser a mesma qualificação CV que, ou maior qualificação CV que, CV2; Caso contrário, o programa é mal formado.

EDIT: Relembrando, acho que a IBM está certa. Eu estava pensando anteriormente na possibilidade de ter que copiar o temporário, mas essa não é a fonte do problema. Para criar o temporário usando a inicialização da cópia de não referência, conforme especificado em §8.5, ele precisa do ctor. Em particular, neste ponto, é equivalente a uma expressão como:

T x = a;

Isso é basicamente equivalente a:

T x = t (a);

Ou seja, é necessário criar um temporário e copiar o temporário para o objeto que está sendo inicializado (que, neste caso, é também um temporário). Para resumir o processo necessário, é aproximadamente equivalente a codificar como:

T temp1(3);
T temp2(temp1); // requires copy ctor
show(temp2);    // show's reference parameter binds directly to temp2

Outras dicas

C ++ permite compiladores suficientemente inteligentes para evitar copiar objetos temporários, a única violação do Até parece regra permitida pelo padrão. Não estou familiarizado com o compilador AIX C ++ da IBM, mas parece que pensa o show(3) A chamada exige que uma coisa temporária seja copiada. Nesse caso, o C ++ exige que você tenha um construtor de cópia acessível, mesmo que seu compilador seja inteligente o suficiente para evitar usá -lo.

Mas por que faz show(3) requer uma cópia em primeiro lugar? Que eu não consigo descobrir. Com sorte, o LITB estará junto a pouco.

Meu pressentimento é que Jerry's responda está correto, mas ainda existem algumas perguntas.

O interessante é que existe uma questão central que cobre o parágrafo anterior dessa seção (391). Esse problema se relaciona com quando o argumento é o mesmo tipo de classe. Especificamente:

int main () {
  show ( Thing (3) );       // not allowed under current wording
                            // but allowed with Core Issue 391

  show ( 3 );               // Still illegal with 391
}

A mudança na questão central 391 afeta apenas quando o Rvalue temporário tem o mesmo tipo de classe. A redação anterior tinha:

Se a expressão do inicializador for um rvalue, com T2 a tipo de classe e cv1 T1 é compatível com referência com cv2 T2, A referência está vinculada da seguinte forma:

[...]

O construtor que seria usado para fazer a cópia deve ser chamável se a cópia está realmente concluída.

Essa última linha é o que faria show(Thing(3)) ilegal conforme o padrão atual. A redação proposta para esta seção é:

Se a expressão do inicializador for um Rvalue, com T2 A TIPO DE CLASSE, e "CV1 T1" é compatível com "CV2 T2", a referência está ligada ao objeto representado pelo RValue (ver 3.10 [Basic.lval]) ou para um subobjeto dentro desse objeto.

Neste ponto, eu considerei que o G ++ pode ter atualizado seu comportamento conforme 391 mas que a mudança incluiu acidentalmente o caso de inicialização de cópia. No entanto, isso não é demonstrado pelas versões de G ++ com as quais testei:

class A{
public:
  A ();
  A (int);
private:
  A (A const &);
};

void foo (A const &);

void foo ()
{
  A a = 3 ;     // 3.2.3 (ERROR), 3.4.6(ERROR), 4.4.0(ERROR), Comeau(ERROR)

  foo ( 3 ) ;   // 3.2.3 (OK), 3.4.6(OK), 4.4.0(OK), Comeau(OK)
  foo ( A() );  // 3.2.3 (OK), 3.4.6(ERROR), 4.4.0(OK), Comeau(OK)
  foo ( A(3) ); // 3.2.3 (OK), 3.4.6(ERROR), 4.4.0(OK), Comeau(OK)
}

Não consigo encontrar falhas na interpretação de Jerry para o foo (3) Caso, no entanto, tenho dúvidas devido à discrepência entre os diferentes comportamentos do compilador.

O que acontece se você tentar nomear a coisa temporária?

Thing temp(3);
show(temp);

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