Pergunta

O código a seguir funciona apenas quando o construtor de cópia está disponível.

Quando eu adicionar instruções de impressão (via std::cout) e fazer o construtor de cópia disponível não é usada (I supor que há truque tão compilador acontecendo para remover a cópia desnecessário).

Mas em ambos os operator << saída ea plop() função abaixo (onde eu criar um objeto temporário) Eu não vejo a necessidade do construtor de cópia. Alguém pode explicar por que a linguagem é necessário quando eu estou passando tudo por referência const (ou o que estou fazendo de errado).

#include <iostream>

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

std::ostream& operator<<(std::ostream& str,N const& data)
{
    return str << "N\n";
}

void plop(std::ostream& str,N const& data)
{
    str << "N\n";
}

int main()
{
    std::cout << N(1);     // Needs copy constructor  (line 25)
    plop(std::cout,N(1));  // Needs copy constructor

    N    a(5);
    std::cout << a;
    plop(std::cout,a);
}

Compiler:

[Alpha: ~ / X] myork% g ++ -v
Usando built-in especificações.
Alvo: i686-apple-darwin10
Configurado com: / var / tmp / gcc / gcc-5646 ~ 6 / src / configure --disable-verificando --enable-werror --prefix = / usr --mandir = / share / homem --enable-línguas = c , objc, C ++, ++ objc --program-transformar-name = / ^ [CG] [^ .-] * $ / s / $ / - 4,2 / with-slibdir = / usr / lib --build = i686-maçã-darwin10 with-Gxx-incluem-dir = / incluem / c ++ / 4.2.1 --program-prefixo = i686-maçã-darwin10- --host = x86_64-maçã-darwin10 --target = i686- apple-darwin10
modelo Tópico: posix
versão gcc 4.2.1 (Apple Inc. construir 5646)

[Alpha: ~ / X] myork% g ++ T.cpp
T.cpp: Em função ‘int main ()’:
T.cpp: 10: error: ‘N :: N (const N &)’ é
privada T.cpp: 25: error: neste contexto
T.cpp: 10: error: ‘N :: N (const N &)’ é
privada T.cpp: 26: erro: neste contexto

Esta é uma versão simplificada de um código real.
No código real que eu tenho uma classe que contém um std :: auto_ptr. Isto significa que um construtor de cópia que leva uma referência const não é válido (sem algum trabalho) e eu estava ficando um erro indicando que o construtor de cópia não estava disponível por causa disso:

Alterar a classe demasiado:

class N
{
    public:
        N(int)  {}
    private:
        std::auto_ptr<int>  data;
};

O erro é então:

T.cpp: 25: erro: nenhuma função correspondente para chamar para ‘N :: N (N)’

Foi útil?

Solução

A partir http://gcc.gnu.org/gcc-3.4/changes.html

Quando ligar um rvalue do tipo de classe para uma referência, o construtor copiar da classe deve ser acessível. Para exemplo, considere o seguinte código:

class A 
{
public:
  A();

private:
  A(const A&);   // private copy ctor
};

A makeA(void);
void foo(const A&);

void bar(void)
{
  foo(A());       // error, copy ctor is not accessible
  foo(makeA());   // error, copy ctor is not accessible

  A a1;
  foo(a1);        // OK, a1 is a lvalue
}

Isso pode ser surpreendente à primeira vista, especialmente porque mais popular compiladores não implementar corretamente esta regra ( mais detalhes ).

Esta será corrigido em C ++ 1x por núcleo Issue 391 .

Outras dicas

As partes aplicáveis ??da norma aqui estão §8.5.3 / 5, que abrange inicialização de referências e §3.10 / 6, que diz o que é um rvalue eo que é um lvalue (nem sempre óbvio em C ++).

Neste caso, você inicialização expressão é: "N (1)", então você está criando explicitamente um objeto usando a notação funcional. De acordo com 3.10 / 6, essa expressão é um rvalue.

Então nós temos que andar pelas regras em 8.5.3 / 5 em ordem, e usar o primeiro que se aplica. A primeira possibilidade é se a expressão representa um lvalue, ou pode ser implicitamente convertido para um lvalue. Sua expressão é um rvalue, e conversão implícita para um lvalue exigiria uma função de conversão que retorna uma referência, o que não parece existir neste caso, de modo que não parece aplicar-se.

A próxima regra diz que a referência deve ser a um const T (que é o caso aqui). Neste caso, a expressão é um rvalue do tipo de classe e é referência-compatível com a referência (isto é, a referência é a mesma classe, ou uma base da classe). Isso significa que a bala na parte inferior da página 151 (179 do C ++ 2003 PDF) parece aplicar-se. Neste caso, o compilador é permitido para ambos se ligam a referência directamente para o objecto que representa o rvalue, ou criar uma cópia temporária do rvalue, e se ligam a essa cópia temporária.

De qualquer maneira, no entanto, a norma exige explicitamente que: "O construtor que seria usado para fazer a cópia será exigível ou não a cópia é realmente feito"

Como tal, eu acreditam que gcc é direito de dar uma mensagem de erro, e os outros são tecnicamente errado aceitar o código. Eu simplifiquei o código um pouco para o seguinte:

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

void plop(N const& data) { }

int main() {
    plop(N(1));
}

Quando invocado com "--Um" (modo erros estrito), Comeau dá a seguinte mensagem de erro:

"plop.cpp", line 12: error: "N::N(const N &)", required for copy that was
          eliminated, is inaccessible
      plop(N(1));
           ^

Da mesma forma, quando invocado com "/ Za" (sua "ANSI conformidade" mode), VC ++ 9 dá:

plop.cpp
plop.cpp(12) : error C2248: 'N::N' : cannot access private member declared in class 'N'
        plop.cpp(6) : see declaration of 'N::N'
        plop.cpp(2) : see declaration of 'N'
        while checking that elided copy-constructor 'N::N(const N &)' is callable
        plop.cpp(6) : see declaration of 'N::N'
        when converting from 'N' to 'const N &'

Meu palpite é que a maioria dos outros compiladores fazer praticamente o mesmo. Uma vez que eles otimizar a chamada para o construtor de cópia, eles normalmente não requerem que existe ou ser acessível. Quando você pedir-lhes para estar em conformidade com o padrão tão precisa quanto eles podem, eles dão a mensagem de erro, porque é tecnicamente necessário, mesmo que eles não usá-lo.

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