2011-09-12 32 views
23

En C++ 0x, puede utilizar la palabra clave using para heredar constructores, así:¿Los constructores heredados funcionan con plantillas en C++ 0x?

class B { B(int) {} }; 

class A : public B { using B::B; }; 

Qué va a declarar implícitamente una constructora A(int). Funciona esto con plantillas?

class B { B(int) {} }; 

template<class T> class A : public T { using T::T; }; 

Dentro T::T, espero que el compilador para averiguar la mano izquierda T ya que con el operador alcance de argumentos de plantilla es normal, pero tratar de calcular que la mano derecha T es el constructor es un caso especial. De hecho, parece que hay una ambigüedad: ¿y si tengo un método llamado T en B que estoy tratando de agregar sobrecargas en A (así es como un compilador interpretaría una declaración de uso previa a C++ 0x)?

Respuesta

10

Sí, funciona, y el motivo es el mecanismo de búsqueda de nombre. El mecanismo que funciona para la declaración heredadores-constructores es simple: si el nombre de la declaración que usa se refiere a los constructores de la clase base, eso es una declaración de constructores heredables. En 3.4.3.1 [class.qual] p2 encontramos:

En una búsqueda en la que el constructor es un resultado de búsqueda aceptable y el anidado-nombre-especificador nombra a una clase C

  • si el nombre especificado después del especificador de nombre anidado, cuando se busca en C, es el nombre de clase inyectado de C (Cláusula 9) o
  • en una declaración using (7.3.3) que es una declaración de miembro , si el nombre especificado después del especificador de nombre anidado es el mismo que el identificador o el nombre de plantilla de id de plantilla simple en el último componente del especificador de nombre anidado

El nombre es en lugar considerado para nombrar el constructor de la clase C.

Este es el párrafo que hace de clase definiciones constructor funcionan, y este es también el párrafo que hace que heredan declaraciones constructor trabajo.La segunda bala se aplica en este caso:

struct B { 
    B(int) { } 
}; 

typedef B mytype; 

struct A : B { 
    // "name ... is the same as the identifier ... in the last component ..." 
    using mytype::mytype; 
}; 


template<typename T> using same = T; 

struct C : B { 
    // "name ... is the same as the template-name ... in the last component ..." 
    same<B>::same; 
}; 

El último ejemplo demuestra también útil en casos tales como los siguientes

template<template<typename> class Base> 
struct X : Base<int> { 
    using Base<int>::Base; 
}; 

En resumen:

  • La primera bala anterior es una regla semántica: si el nombre después del especificador de nombre anidado se refiere al nombre de la clase inyectada (B::B o mytype::B), se traducirá para referirse al constructo o (s)

  • La segunda bala es una regla sintáctica - los nombres simplemente deben coincidir - su significado es irrelevante lo contrario - que podría haber sido un miembro de llama Base en el argumento plantilla proporcionada a X, como en los siguientes, pero el uso de declaración todavía importar los constructores y hacer no nombre del miembro Base:

    template<typename T> struct D { private: T Base; }; 
    X<D> x; // valid, the private member is *not* touched! 
    
7

Sí, parece que lo hace, de la norma (feb 2011 Borrador), sección 12.9:

template< class T > 
struct D : T { 
using T::T; // declares all constructors from class T 
~D() { std::clog << "Destroying wrapper" << std::endl; } 
}; 

plantilla de clase D envuelve ninguna clase delante y hacia todos sus constructores, mientras escribe un mensaje al registro estándar cuando se destruye un objeto de clase D. -fin ejemplo

Otra cosa a destacar, mientras que la norma permite, de acuerdo con this list, sólo el 1 compilador, IBM XLC++, es compatible con esta característica en una versión de lanzamiento. GCC solo lo admite actualmente con un parche.

Editar: AJG85 señaló que la T en la plantilla siempre se refiere al marcador de posición, por lo que el 'uso de T :: T' siempre se refiere al argumento de la plantilla.

+1

sin que estaría bien porque 'nombretipo t' no representa un método llamado T representa un marcador de posición para el símbolo de tipo usado por la instanciación de la plantilla. Por ejemplo 'A ' daría como resultado 'using int :: int; '... palabra de advertencia: ¿qué sucede con un tipo que no pretende ser una clase base como un contenedor STL? – AJG85

+0

Tiene razón en la pregunta variable de miembro. En cuanto a la herencia de un contenedor STL, funcionaría, pero estás en riesgo si usaras el tipo de forma polimórfica. Pero si simplemente está creando D >, y luego usarlo así en un solo lugar, entonces funcionaría. No es una buena práctica, pero funcionaría. –

+0

Correcto pero definitivamente es algo a tener en cuenta ya que agregar estado al objeto derivado o utilizar punteros de clase base puede introducirse accidentalmente ya que la plantilla ofusca de algún modo la herencia y la causa raíz del problema más adelante. – AJG85

Cuestiones relacionadas