2010-05-05 32 views
11
class Foo 
{ 
public: 
    explicit Foo() {} 
    explicit Foo(Foo&) {} 
}; 

Foo d = Foo(); 

error: no matching function for call to 'Foo::Foo(Foo)'Foo f = Foo(); // no hay función de coincidencia para la llamada a 'Foo :: Foo (Foo)' ... ¿eh?

He intentado cambiar Foo(Foo&)-Foo(Foo) como sugiere el error, que yo sepa no es un constructor válida, y por supuesto me sale:

error: invalid constructor; you probably meant ‘Foo (const Foo&)’

lo que da? ¿Cómo resuelvo esto? (Esto está en GCC por cierto)

+1

Foo Foo Foo? ¿Huh? –

+2

El compilador ya respondió su pregunta ... 'Foo (const Foo &)'. 'Foo d = Foo();' invoca el constructor de copia. –

+3

+1 porque nadie parece saber la respuesta –

Respuesta

12

Hay dos cosas cuestionables que tiene en su constructor de copia.

En primer lugar, usted ha hecho constructor de copia explícita (lo cual es algo cuestionable que hacer), por lo que lo haría (en teoría) tiene que hacer:

Foo d((Foo())); 

En segundo lugar, su copia constructor toma una referencia y no una referencia const lo que significa que no puede usarlo con un Foo temporal.

Personalmente, me gustaría eliminar explicit del constructor de copias y hacer que tome una referencia const si es posible.

Tenga en cuenta que el explicit en su constructor predeterminado no tiene ningún efecto. [*] explicit solo tiene un efecto en los constructores que pueden invocarse con un solo parámetro. Impide que se usen para conversiones implícitas. Para los constructores que toman solo cero o solo dos o más parámetros, no tiene ningún efecto.

[Nota: no puede haber una diferencia entre:.

Foo d; 

y

Foo d = Foo(); 

pero en este caso tiene un constructor por defecto declarado el usuario por lo que este no se aplica]

Editar: [*] Acabo de comprobar esto dos veces y 12.3.1 [class.conv.ctor] dice que puede hacer una configuración predeterminada tructor explicit. En este caso, el constructor se usará para realizar la inicialización predeterminada o value-initialization.Para ser sincero, no entiendo el valor de esto como si tuviera un constructor declarado por el usuario, entonces es un tipo que no es POD e incluso los objetos locales de tipo no POD son inicializados por defecto si no tienen un inicializador que esta cláusula dice puede ser hecha por un constructor predeterminado explicit. Quizás alguien pueda señalar un caso de esquina en el que sí hace una diferencia, pero por ahora no veo qué efecto tiene explicit en un constructor predeterminado.

+1

¿Por qué el conjunto adicional de paréntesis? – clahey

+2

@clahey: para evitar el análisis más irritante. –

+4

Ahora que James me ha dado la respuesta correcta, puedo decir: porque soy un fanático de lisp de closet. –

2

Tu problema está en la creación de instancias. No necesita Foo d = Foo(); para un constructor predeterminado.

Mantenga su clase lo mismo, pero a probar esto para la instanciación:

Foo d; 

De hecho, usted ni siquiera necesita Foo d = Foo(arguments); para la construcción con los parámetros. Esto debería ser así:

Foo d(arguments); 
+0

'Foo d = Foo()' fue solo una simplificación para generar el error. Podría haber tenido 'Foo d;' y 'Foo k = d;' en algún otro lado – Kyle

+0

@Kyle: De los muchos comentarios que has dejado, puedo ver que deliberadamente intentabas invocar el constructor de copia. Ojalá lo hubieras dicho tan explícitamente en tu pregunta. No puedo encontrar en ninguna parte de tu pregunta que insinúas que incluso sabes lo que es un constructor de copia * *; acabas de publicar el código y dijiste "¿por qué es brookens?" Hice la suposición (sí, sí) de que simplemente lo estabas haciendo mal. 'Foo d = Foo(); 'sin duda invoca el constructor de copia en esa fugaz instancia' Foo', pero es algo que nunca deberías * hacer *. – Randolpho

+0

Tal como aparece implícito en su título, mi pregunta es sobre el extraño mensaje de error, que no parece ser un resultado obvio del código dado. Todavía tengo que dominar la habilidad de arrinconar una pregunta con tanta precisión que nadie puede responder la pregunta equivocada. Por favor edite su respuesta para que SO me permita invitarlo a usted. – Kyle

2
Foo d = Foo(); 

debería ser

Foo d; 

La primera línea crea una instancia de Foo y luego lo copia en d;

+0

Correcto, pero no una respuesta a la pregunta: 'Foo d = Foo()' fue solo una simplificación para generar el error. Pude haber tenido 'Foo d;' y 'Foo k = d;' en otro lado. – Kyle

3

Prueba sin el explícito? Creo que:

Foo foo = Foo() 

crea una copia implícita, por lo que el constructor de copia explícita no se desencadena.

Editar:

Esto es solo la mitad de la respuesta. Consulte la publicación de Charles Bailey o UncleBens para saber por qué es necesario const.

+1

Esta es la respuesta correcta. Además, no hay ninguna razón para hacer que el constructor de copia sea explícito para comenzar. Los constructores explícitos están destinados a evitar que accidentalmente, implícitamente, se conviertan tipos. –

+1

Esta es la mitad de la respuesta. – clahey

1

El compilador le está diciendo ... Utilice esta:

Foo(const Foo&) {} 
+0

Contrarrestado por voto. Está diciendo literalmente cómo debería ser la firma. (No es lo que debería hacer, nada). – UncleBens

5

Usted no desea marcar cualquiera de esos constructores como explícita - el compilador tiene que usar los dos, en particular el constructor de copia, implícitamente. ¿Qué estás tratando de lograr marcándolos explícitamente?

+0

Para responder a su pregunta: Quiero eliminar cualquier sorpresa provocada por una conversión que no quisiera que ocurriera (... haciendo que el compilador genere un error). – Kyle

+1

@Kyle Pero usted quiere que el compilador pueda hacer copias, eso no es una conversión. Y tampoco es la construcción predeterminada. –

+0

Correcto, pero quiero que bombardee si alguna vez trato de hacer 'Foo f = Bar()' donde 'Bar' felizmente se permite convertir a un Foo proporcionando un' operador Foo() '(creo que tengo ese proceso de pensamiento correcto ...) – Kyle

0

Puede solucionar el problema de dos maneras. Uno (ya sugerido por Randolpho) es eliminar el uso del copiador. La otra es escribir un copiador apropiado:

Foo (Foo const &) {} 

Por lo general, desea hacer ambas cosas.

Editar: En mi opinión, mi último comentario es fácil de malinterpretar. Muy pocas clases hacen no necesitan un copiador, pero si necesita un copiador, normalmente debe tener el formulario de arriba (no explícito, y tomando como referencia el parámetro).

+0

'Foo (Foo &)' es un constructor de copia apropiado. Simplemente no es el constructor de copia correcto para lo que está tratando de hacer. –

+0

@Dennis: si te gusta, está bien, pero un constructor de copia que abre la puerta para modificar el objeto que se está copiando no es lo que yo llamaría "correcto". –

-1
class Foo 
{ 
public: 
    explicit Foo() {} 
    explicit Foo(const Foo&) {} 
}; 

Foo d = Foo() 
+0

¿por qué negativo 1? – Betamoo

+0

no fui yo, pero puedo decir que todavía no compila. – Kyle

3

Un constructor de copia no debe ser explícita (que lo hace uncallable aquí y en muchos otros contextos perfectamente razonables, como por ejemplo al adelantar o regresar por valor).

A continuación debe tomar el argumento const referencia, ya que de lo contrario no se puede unir a los temporales.

Foo f = Foo(); 
     ^^^^^ 
      | 
      --- this is a temporary that cannot be passed to a function 
       that accepts a non-const reference 

Por otra parte, no hay ninguna razón para que el constructor por defecto explícita: esta palabra clave sólo tiene sentido para los constructores (que no sea el constructor de copia) que se pueden llamar con exactamente un argumento, en cuyo caso se evita conversiones implícitas de otros tipos en Foo a través de ese constructor.Por ejemplo, si un constructor tomando un int eran explícita, situaciones como estas no se compilará:

Foo f; 
f = 1; //assuming no operator= overload for (types convertible from) int 
     //this implicitly performs f = Foo(1); 

Foo g = 10; 

void x(Foo); 
x(20); 

En definitiva:

class Foo 
{ 
public: 
    Foo(); 
    Foo(const Foo&); 
    //... 
}; 

Foo x = Foo(); 

Y además, si ninguno de los constructores está destinado a hacer cualquier cosa, no es necesario definirlos en absoluto; el compilador los proporcionará automáticamente (si define otros constructores, el constructor predeterminado no se generará automáticamente, sin embargo).

+0

Esta es la mejor respuesta hasta ahora, pero agregaría más texto sobre lo que es correcto hacer. – clahey

+0

Puede invocar un constructor de copia explícito utilizando la inicialización directa (por ejemplo, 'Foo f; Foo g (f);'). –

+0

@James: Gracias, agregué una aclaración. – UncleBens

4

En primer lugar, ni el constructor predeterminado ni el constructor de copia deben ser explicit. Solo necesita hacer un constructor explicit si toma un único argumento de otro tipo, para evitar la conversión implícita de ese tipo. El constructor de copia toma una referencia a la clase en sí, por lo que no hay peligro de una conversión no deseada.

En segundo lugar, asegúrese de que el constructor de copia toma una referencia const.

En tercer lugar, Foo f; es la forma correcta de tener un objeto de clase foo predeterminado. Tenga en cuenta que Foo f(); es incorrecto, porque el compilador lo interpretará como una declaración de la función f() que devuelve un objeto de la clase Foo.

En cuarto lugar, si ha escrito su propio constructor de copias, entonces también debe escribir el operador de asignación.

 

class Foo 
{ 
    Foo() {} // no need to make explicit. Nothing to convert from. 

    Foo(const &Foo f) {} // again, nothing wrong with conversion from Foo to Foo 

    explicit Foo(int a) {} // need explicit to prevent accidental passing of an int 
          // to a function that takes Foo as an argument 
}; 
 
Cuestiones relacionadas