2010-06-25 9 views
17

¿Cuál es la forma correcta de reenviar todos los constructores principales en C++ 0x?Reenvío de todos los constructores en C++ 0x

que he estado haciendo esto:

class X: public Super { 
    template<typename... Args> 
     X(Args&&... args): Super(args...) {} 
}; 
+0

¿Hay alguna razón para no escribir todos los constructores en lugar de utilizar un atajo de plantilla? –

+10

Sí, es hora, y si la clase Super se actualiza, entonces estará en un lío de mantenimiento. La solución automatizada es infinitamente superior. – Puppy

+1

@DeadMG - En realidad no. Entonces, ¿qué pasa si la base obtiene un nuevo constructor? El derivado no necesita usarlo a menos que se requiera la nueva información. En este punto, esta "solución" automatizada simplemente empuja a tratar con esto a todos los clientes derivados. En la mayoría de los casos, esto es completamente incorrecto. Derived no debe actualizarse a menos que haya algún objeto que lo necesite y debe hacerse de manera explícita para que sea obvio cómo construir el objeto mirando su constructor. Reserve las plantillas variadic para condiciones variadic. –

Respuesta

39

hay una mejor manera en C++ 0x para este

class X: public Super { 
    using Super::Super; 
}; 

Si se declara una plantilla perfecta de reenvío, el tipo se comporta mal en la resolución de sobrecarga. Imagine que su clase base es convertible a partir int y existen dos funciones para imprimir clases

class Base { 
public: 
    Base(int n); 
}; 

class Specific: public Base { 
public: 
    template<typename... Args> 
    Specific(Args&&... args); 
}; 

void printOut(Specific const& b); 
void printOut(std::string const& s); 

Usted lo llama con

printOut("hello"); 

lo que se llamará? Es ambiguo, porque Specific puede convertir cualquier argumento, incluidas las matrices de caracteres. Lo hace así sin tener en cuenta los constructores existentes de la clase base. Heredar constructores con declaraciones de uso solo declara los constructores necesarios para que esto funcione.

+0

en mi humilde opinión - esta es la respuesta que debería haber sido aceptada, aunque el otro responde directamente a la pregunta. –

+0

Gracias Johannes. @Noah Roberts: Sí. –

+1

Me pregunto si la palabra clave 'explicit' no hace que la llamada sea inequívoca y al mismo tiempo permite que funcione la plantilla de reenvío perfecta. No es que recomiende generar automágicamente constructores derivados ... –

5

Yo creo lo que necesita hacer Super(std::forward<Args>(args)...) si quieres cosas reenviados correctamente. args tiene un nombre, por lo que creo que se vinculará a las referencias regulares antes de referencias rvalue.

Sin embargo, lo más importante es que no reenviará los constructores de copias de esta manera. El compilador generará uno para usted, porque no considera los constructores de plantillas. En el ejemplo proporcionado, está bien, porque el compilador generado solo llamará al constructor de copia padre de todos modos, que es exactamente lo que quiere. Si eso no es apropiado en un caso más complicado, entonces tendrá que escribirlo usted mismo.

+1

'Super (std :: forward (args) ...)' es correcto, por lo que estuvo muy cerca. Recuerde, reenviar requiere un argumento de plantilla explícito. – Puppy

+0

Lo tenía originalmente, luego lo adiviné.Gracias por la corrección. –

+0

Esto es genial. ¿Tienes una intuición sobre lo que hace std :: forward? (¿Es de primer orden?) ¿Por qué no podemos simplemente escribir "args ..."? ¿Eso daría lugar a que se llamen a los constructores de copia? –