Member templates
Las declaraciones de plantilla ( class , function , y variables (since C++14) ) pueden aparecer dentro de una especificación de miembro de cualquier class, struct o union que no sean clases locales .
#include <algorithm> #include <iostream> #include <string> #include <vector> struct Printer { // funtor genérico std::ostream& os; Printer(std::ostream& os) : os(os) {} template<typename T> void operator()(const T& obj) { os << obj << ' '; } // plantilla de miembro }; int main() { std::vector<int> v{1,2,3}; std::for_each(v.begin(), v.end(), Printer(std::cout)); std::string s{"abc"}; std::ranges::for_each(s, Printer(std::cout)); }
Salida:
1 2 3 a b c
Las especializaciones parciales de plantillas de miembros pueden aparecer tanto en el ámbito de la clase como en el ámbito del espacio de nombres envolvente. Las especializaciones explícitas pueden aparecer en cualquier ámbito en el que pueda aparecer la plantilla principal.
struct A { template<class T> struct B; // plantilla de miembro primaria template<class T> struct B<T*> {}; // OK: especialización parcial // template<> struct B<int*> {}; // OK via CWG 727: especialización completa }; template<> struct A::B<int*> {}; // OK template<class T> struct A::B<T&> {}; // OK
Si la declaración de la clase envolvente es, a su vez, una plantilla de clase, cuando una plantilla de miembro se define fuera del cuerpo de la clase, toma dos conjuntos de parámetros de plantilla: uno para la clase envolvente y otro para sí misma:
template<typename T1> struct string { // función de plantilla miembro template<typename T2> int compare(const T2&); // los constructores también pueden ser plantillas template<typename T2> string(const std::basic_string<T2>& s) { /*...*/ } }; // definición externa de string<T1>::compare<T2> template<typename T1> // para la plantilla de clase contenedora template<typename T2> // para la plantilla miembro int string<T1>::compare(const T2& s) { /* ... */ }
Contenidos |
Plantillas de funciones miembro
Destructores y constructores de copia no pueden ser plantillas. Si se declara un constructor plantilla que podría instanciarse con la firma de tipo de un constructor de copia, se utiliza en su lugar el constructor de copia declarado implícitamente .
Una función miembro plantilla no puede ser virtual, y una función miembro plantilla en una clase derivada no puede anular una función miembro virtual de la clase base.
class Base { virtual void f(int); }; struct Derived : Base { // esta plantilla de miembro no reemplaza Base::f template<class T> void f(T); // el reemplazo de miembro no plantilla puede llamar a la plantilla: void f(int i) override { f<>(i); } };
Se pueden declarar una función miembro no plantilla y una función miembro plantilla con el mismo nombre. En caso de conflicto (cuando alguna especialización de plantilla coincide exactamente con la firma de la función no plantilla), el uso de ese nombre y tipo se refiere a la función miembro no plantilla a menos que se proporcione una lista explícita de argumentos de plantilla.
template<typename T> struct A { void f(int); // miembro no plantilla template<typename T2> void f(T2); // plantilla de miembro }; // definición de miembro plantilla template<typename T> template<typename T2> void A<T>::f(T2) { // algún código } int main() { A<char> ac; ac.f('c'); // llama a la función plantilla A<char>::f<char>(char) ac.f(1); // llama a la función no plantilla A<char>::f(int) ac.f<>(1); // llama a la función plantilla A<char>::f<int>(int) }
Una definición fuera de la clase de una plantilla de función miembro debe ser
equivalente
a la declaración dentro de la clase (consulte
sobrecarga de plantillas de función
para la definición de equivalencia), de lo contrario se considera una sobrecarga.
struct X { template<class T> T good(T n); template<class T> T bad(T n); }; template<class T> struct identity { using type = T; }; // CORRECTO: declaración equivalente template<class V> V X::good(V n) { return n; } // ERROR: no equivalente a ninguna de las declaraciones dentro de X template<class T> T X::bad(typename identity<T>::type n) { return n; }
Plantillas de funciones de conversión
Una función de conversión definida por el usuario puede ser una plantilla.
struct A { template<typename T> operator T*(); // conversión a puntero de cualquier tipo }; // definición fuera de la clase template<typename T> A::operator T*() { return nullptr; } // especialización explícita para char* template<> A::operator char*() { return nullptr; } // instanciación explícita template A::operator void*(); int main() { A a; int* ip = a.operator int*(); // llamada explícita a A::operator int*() }
Durante la resolución de sobrecarga , las especializaciones de plantillas de funciones de conversión no se encuentran mediante búsqueda por nombre . En su lugar, se consideran todas las plantillas de funciones de conversión visibles, y cada especialización producida por deducción de argumentos de plantilla (que tiene reglas especiales para plantillas de funciones de conversión) se utiliza como si se hubiera encontrado mediante búsqueda por nombre.
Las declaraciones using en clases derivadas no pueden hacer referencia a especializaciones de funciones de conversión de plantilla de clases base.
|
Una función de conversión definida por el usuario no puede tener un tipo de retorno deducido: struct S { operator auto() const { return 10; } // OK template<class T> operator auto() const { return 42; } // error }; |
(desde C++14) |
Plantillas de variables miembroUna declaración de plantilla de variable puede aparecer en el ámbito de clase, en cuyo caso declara una plantilla de miembro de datos estático. Consulte plantillas de variables para más detalles. |
(desde C++14) |
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 1878 | C++14 | operator auto estaba técnicamente permitido | operator auto prohibido |