Espacios de nombres
Variantes
Acciones

Plantillas de miembro

De cppreference.com
< cpp‎ | language

Las declaraciones de plantillas (de clase, de función, y de variables (desde C++14)) pueden aparecer dentro de una especificación de miembro de cualquier clase, estructura o unión que no sean clases locales.

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
 
struct Printer { // objeto función genérico
    std::ostream& os;
    Printer(std::ostream& os) : os(os) {}
    template<typename T>
    void operator()(const T& obj) { os << obj << ' '; } // plantilla 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 miembro pueden aparecer tanto en el ámbito de la clase como en ámbito del espacio de nombres adjunto. 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 miembro primaria
    template<class T> struct B<T*> { }; // de acuerdo: especialización parcial
//  template<> struct B<int*> { };      // de acuerdo vía CWG 727: especialización total
};
template<> struct A::B<int*> { };       // de acuerdo
template<class T> struct A::B<T&> { };  // de acuerdo

Si la declaración de la clase adjunta es, a su vez, una plantilla de clase, cuando se define una plantilla miembro fuera del cuerpo de la clase, se toman dos conjuntos de parámetros de plantilla: uno para la clase adjunta y otro para sí misma:

template<typename T1>
struct string 
{
    // plantilla de función 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) { /*...*/ }
};
// fuera de la definición de clase de string<T1>::compare<T2> 
template<typename T1> // para la plantilla de clase adjunta
template<typename T2> // para la plantilla miembro
int string<T1>::compare(const T2& s) { /* ... */ }

Contenido

[editar] Plantillas de función miembro

Los destructores y los constructores de copia no pueden ser plantillas. Si se declara un constructor de plantilla que podría instanciarse con la signatura de tipo de un constructor de copia, en su lugar se usa el constructor de copia declarado implícitamente.

Una plantilla de función miembro no puede ser virtual y una plantilla de función miembro en una clase derivada no puede sobreescribir una función miembro virtual de la clase base.

class Base 
{
    virtual void f(int);
};
struct Derived : Base 
{
    // esta plantilla miembro no reemplaza a Base::f
    template <class T> void f(T);
 
    // miembro que no es de plantilla, que reemplaza, puede llamar a la plantilla:
    void f(int i) override {
         f<>(i);
    }
};

Se puede declarar una función miembro que no sea de plantilla y una función miembro de plantilla con el mismo nombre. En caso de conflicto (cuando alguna especialización de plantilla coincide exactamente con la signatura de la función que no es de plantilla), el uso de ese nombre y tipo se refiere al miembro que no es de plantilla a menos que se proporcione una lista explícita de argumentos de plantilla.

template<typename T>
struct A 
{
    void f(int); // miembro que no es de plantilla
 
    template<typename T2>
    void f(T2); // plantilla miembro
};
 
// definición de plantilla miembro
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 de plantilla A<char>::f<char>(char)
    ac.f(1);   // llama a la función que no es de plantilla A<char>::f(int)
    ac.f<>(1); // llama a la función de 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 (véase sobrecarga de plantillas de función para la definición de equivalencia), de lo contrario, se considera que es 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; };
 
// de acuerdo: declaración equivalente
template<class V>
V X::good(V n) { return n; }
 
// ERROR: no es equivalente a ninguna de las declaraciones dentro de X
template<class T>
T X::bad(typename identity<T>::type n) { return n; }

[editar] Plantillas de función 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 resolución de sobrecarga, las especializaciones de plantillas de función de conversión no se encuentran en búsqueda por nombres . En su lugar, se consideran todas las plantillas de función de conversión visibles, y cada especialización producida por la deducción de argumentos de plantilla (que tiene reglas especiales para las plantillas de función de conversión) se usa como si se encontrara por búsqueda por nombre.

Las declaraciones using en las clases derivadas no pueden referirse a las especializaciones de las plantillas de función de conversión de las clases base.

Una plantilla de función de conversión definida por usuario no puede tener un tipo de retorno deducido:

struct S 
{
  operator auto() const { return 10; } // de acuerdo
  template<class T> operator auto() const { return 42; } // ERROR
};
(desde C++14)

Plantillas de variable miembro

Una declaración de plantilla de variable puede aparecer en el ámbito de la clase, en cuyo caso declara una plantilla de miembro de datos estático. Véase plantillas de variable para más detalles.

(desde C++14)

[editar] Informes de defectos

Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.

ID Aplicado a Comportamiento según lo publicado Comportamiento correcto
CWG 1878 C++14 Se permitía técnicamente el operador auto Se prohibió el operador auto