Anda di halaman 1dari 17

5.

1 El atributo dinmico es heredable


Lo mismo que en las funciones virtuales, el atributo dinamic tambin es heredado
automticamente por las subclases, lo que puede ser comprobado ejecutando el ejemplo anterior.
Cuando se genera una salida en ensamblador mediante el comando -S del compilador C++Borland
("bcc32 -S") pueden examinarse las tablas virtuales de las clases func1, func2, y func3,
donde puede comprobarse como func2 no tiene entrada para la funcin dinmica dynbar, pero si
para virtbar. A pesar de lo cual, puede invocarse dynbar en el objeto instanciado de la
clasefunc2.

5.2 Las funciones dinmicas no pueden ser virtuales y viceversa


No est permitido redefinir una funcin virtual para que sea dinmica; as mismo, tampoco puede
redefinirse una funcin dinmica para que seavirtual. El ejemplo que sigue produce errores:
#include <vcl.h>
#include <stdio.h>
class __declspec(vclclass) func1 : public TObject {
public:
func1() { }
int virtual virtbar() { return 5; }
int __declspec(dynamic) dynbar() { return 5; }
};
class __declspec(vclclass) func2 : public foo1 {
public:
func2() { }
int __declspec(dynamic) virtbar() { return 10; }
int virtual dynbar() { return 10; }
};
Resultado de la compilacin
Error:
Error:

Cannot override a virtual with a dynamic function


Cannot override a dynamic with a virtual function

4.11.8c Clases abstractas


1 Sinopsis
La abstraccin es un recurso de la mente (quizs el ms caracterstico de nuestra pretendida
superioridad respecto del mundo animal). Por su parte, los lenguajes de programacin permiten
expresar la solucin de un problema de forma comprensible simultneamente por la mquina y el
humano. Constituyen un puente entre la abstraccin de la mente y una serie de instrucciones
ejecutables por un dispositivo electrnico. En consecuencia, la capacidad de abstraccin es una
caracterstica deseable de los lenguajes artificiales, pues cuanto mayor sea, mayor ser su
aproximacin al lado humano. Es decir, con la imagen existente en la mente del programador. En
este sentido, la introduccin de las clases en los lenguajes orientados a objetos ha representado
un importante avance respecto de la programacin tradicional y dentro de ellas, las
denominadasclases abstractas
son las que representan el mayor grado de abstraccin.

De hecho, las clases abstractas presentan un nivel de "abstraccin" tan elevado que no sirven
para instanciar objetos de ellas. Representan los escalones ms elevados de algunas jerarquas de
clases y solo sirven para derivar otras clases, en las que se van implementando detalles y
concreciones, hasta que finalmente presentan un nivel de definicin suficiente que permita
instanciar objetos concretos. Se suelen utilizar en aquellos casos en que se quiere que una serie
de clases mantengan una cierta caracterstica o interfaz comn. Por esta razn a veces se dice de
ellas que son pura interfaz.
Resulta evidente en el ejemplo de la figura que los diversos tipos de motores tienen caractersticas
diferentes. Realmente tienen poco en comn un motor elctrico de corriente alterna y una turbina
de vapor. Sin embargo, la construccin de una jerarqua en la que todos motores desciendan de un
ancestro comn, la clase abstracta "Motores", presenta la ventaja de unificar la interfaz. Aunque
evidentemente su definicin ser tan "abstracta", que no pueda ser utilizada para instanciar
directamente ningn tipo de motor. El creador del lenguaje dice de ellas que soportan la nocin de
un concepto general del que solo pueden utilizarse variantes ms concretas [2].

2 Clases abstractas
Una clase abstracta es la que tiene al menos una funcin virtual pura (como hemos visto,
una funcin virtual es especificada como "pura" hacindola igual a cero
4.11.8a).
Nota: recordemos que las clases que tienen al menos una funcin virtual (o virtual pura) se
denominan clases polimrficas (
4.11.8). Resulta por tanto, que todas las clases
abstractas son tambin polimrficas, pero no necesariamente a la inversa.

3 Reglas de uso:

Una clase abstracta solo puede ser usada como clase base para otras clases,
pero no puede ser instanciada para crear un objeto 1.
Una clase abstracta no puede ser utilizada como argumento o como retorno de una
funcin 2.
Si puede declararse punteros-a-clase abstracta 3 [1].
Se permiten referencias-a-clase abstracta, suponiendo que el objeto temporal no es
necesario en la inicializacin 4.

4 Ejemplo

class Figura {
// clase abstracta (CA)
point centro;
...
public:
getcentro() { return center; }
mover(point p) { centro = p; dibujar(); }
virtual void rotar(int) = 0;
// funcin (mtodo) virtual pura
virtual void dibujar() = 0;
// funcin (mtodo) virtual pura
void brillo(int);
// mtodo normal -definicin off-line...
};
...
Figura x;
// ERROR: intento de instanciar una CA.1
Figura* sptr;
// Ok: puntero a CA. 3
Figura f();
// ERROR: funcin NO puede devolver tipo CA. 2
int g(Figura s);
// ERROR: CA NO puede ser argumento de funcin 2
Figura& h(Figura&); // Ok: devuelve tipo "referencia-a-CA" 4
int h(Figura&);
// Ok: "referencia-a-CA" si puede ser argumento 4

5 Suponiendo que A sea una clase abstracta y que D sea una clase derivada inmediata de ella,
cada funcin virtual pura fvp de A, para la que Dno aporte una definicin, se convierte en funcin
virtual pura para D. En cuyo caso, D resulta ser tambin una clase abstracta.
Por ejemplo, suponiendo la clase Figura definida previamente:
class Circulo : public Figura { // Circulo deriva de una C.A.
int radio;
// privado por defecto
public:
void rotar(int);
// convierte rotar en funcin no virtual
};
En esta clase, el mtodo heredado Circulo::dibujar() es una funcin virtual pura. Sin
embargo, Circulo::rotar() no lo es (suponemos que definicin se efecta off-line). En
consecuencia, Circulo es tambin una clase abstracta. En cambio, si hacemos:
class Circulo : public Figura { // Circulo deriva de una C.A.
int radio;
public:
void rotar(int);
// convierte rotar en funcin no virtual pura
void dibujar();
// convierte dibujar en funcin no virtual pura
};
la clase Circulo deja de ser abstracta.

6 Las funciones-miembro pueden ser llamadas desde el constructor de una clase abstracta,
pero la llamada directa o indirecta de una funcin virtual pura desde tal constructor puede provocar
un error en tiempo de ejecucin. Sin embargo, son permitidas disposiciones como la siguiente:

class CA {
// clase abstracta
public:
virtual void foo() = 0; // foo virtual pura
CA() {
// constructor
CA::foo(); // Ok.
};
...
void CA::foo() { // definicin en algn sitio
...
}
La razn es la ya sealada (
4.11.8a), de que la utilizacin del operador :: de acceso a mbito
anula el mecanismo de funciones virtuales.

4.12 Plantillas
1 Introduccin
Hemos indicado (
1.1) que en la programacin clsica exista una clara diferenciacin entre los
datos y su manipulacin, es decir, entre los datos y el conjunto de algoritmos para manejarlos. Los
datos eran tipos muy simples (
2.2.1) y generalmente los algoritmos estaban agrupados en
funciones orientadas de forma muy especfica a los datos que deban manejar.
Posteriormente la POO introdujo nuevas facilidades; la posibilidad de extender el concepto de dato,
permitiendo que existiesen tipos ms complejos a los que se poda asociar la operatoria necesaria.
Esta nueva habilidad fue perfilada con un par de mejoras adicionales: la posibilidad de ocultacin
de determinados detalles internos irrelevantes para el usuario, y la capacidad de herencia simple o
mltiple [1].
Observe que las mejoras introducidas por la POO se pueden sintetizar en tres
palabras: composicin, ocultacin y herencia. De otro lado, la posibilidad de incluir juntos los
datos y su operatoria no era exactamente novedosa. Esta circunstancia ya exista de forma
subyacente en todos los lenguajes. Recuerde que el concepto de entero (int en C) ya incluye
implcitamente todo un lgebra y reglas de uso para dicho tipo. Observe tambin que la POO
mantiene un paradigma de programacin orientado al dato (o estructuras de datos). De hecho los
"Objetos" se definen como instancias concretas de las clases y estas representan nuevos tipos-dedatos, de modo que POO es sinnimo de Programacin Orientada a Tipos-de-datos [2].

2 Programacin genrica
Desde luego la POO supuso un formidable avance del arsenal de herramientas de programacin.
Incluso en algunos casos, un autntico baln de oxgeno en el desarrollo y mantenimiento de
aplicaciones muy grandes, en las que se estaba en el lmite de lo factible con las tcnicas
programacin tradicional. Sin embargo, algunos tericos seguan centraron su atencin en los
algoritmos. Algo que estaba ah tambin desde el principio. Se dieron cuenta que frecuentemente
las manipulaciones contienen un denominador comn que se repite bajo apariencias diversas. Por
ejemplo, la idea de ordenacin "Sort" se repite infinidad de veces en la programacin, aunque los
objetos a ordenar y los criterios de ordenacin varen de un caso a otro. Alrededor de esta idea
surgi un nuevo paradigma denominado programacin genrica o funcional.
La programacin genrica est mucho ms centrada en los algoritmos que en los datos y su
postulado fundamental puede sintetizarse en una palabra: generalizacin. Significa que, en la

medida de lo posible, los algoritmos deben ser parametrizados al mximo y expresados de la forma
ms independiente posible de detalles concretos, permitiendo as que puedan servir para la mayor
variedad posible de tipos y estructuras de datos.
Los expertos consideran que la parametrizacin de algoritmos supone una aportacin a las
tcnicas de programacin, al menos tan importante, como fue en su momento la introduccin del
concepto de herencia, y que permite resolver algunos problemas que aquella deja sin solucin.
Observe que la POO y la programacin genrica representan enfoques en cierta forma ortogonales
entre si:
La programacin orientada al dato razona del siguiente modo: representemos un tipo de dato
genrico (por ejemplo int) que permita representar objetos con ciertas caractersticas comunes
(peras y manzanas). Definamos tambin que operaciones pueden aplicarse a este tipo (por
ejemplo aritmticas) y sus reglas de uso, independientemente que el tipo represente peras o
manzanas en cada caso.
Por su parte la programacin funcional razona lo siguiente: construyamos un algoritmo genrico
(por ejemplo sort), que permita representar algoritmos con ciertas caractersticas comunes
(ordenacin de cadenas alfanumricas y vectores por ejemplo). Definamos tambin a que tipos
pueden aplicarse a este algoritmo y sus reglas de uso, independientemente que el algoritmo
represente la ordenacin de cadenas alfanumricas o de vectores.
Con el fin de adoptar los paradigmas de programacin entonces en vanguardia, desde sus inicios
C++ haba adoptado conceptos de lenguajes anteriores. Uno de ellos, la programacin
estructurada [5], ya haba sido recogida en el diseo de su antecesor directo C. Tambin adopt los
conceptos de la POO entonces emergente (
0.Iw1). Posteriormente ha incluido otros conceptos
con que dar soporte a los nuevos enfoques de la programacin funcional;
bsicamente plantillas y contenedores. Las plantillas, que se introdujeron con la versin del
Estndar de Julio de 1998 son un concepto tomado de Ada. Los contenedores no estn definidos
en el propio lenguaje, sino en la Librera Estndar.

3 Sinopsis
Las plantillas ("Templates"), tambin denominadas tipos parametrizados, son un mecanismo C+
+ que permite que un tipo pueda ser utilizado como parmetro en la definicin de una clase o una
funcin.
Ya se trate de clases o funciones, la posibilidad de utilizar un tipo como parmetro en la definicin,
posibilita la existencia de entes de nivel de abstraccin superior al de funcin o clase concreta.
Podramos decir que se trata de funciones o clases genricas; parametrizadas (de ah su nombre).
Las "instancias" concretas de estas clases y funciones conforman familias de funciones o clases
relacionadas por un cierto "denominador comn", de forma que proporcionan un medio simple de
representar gran cantidad de conceptos generales y un medio sencillo para combinarlos.
Por ejemplo, supongamos las dos definiciones siguientes:
void foo (int i) { ... }

// L.1

template < class T >

// L.2

void foo (T t) { ... }

La primera (L.1) es la definicin de una funcin tradicional que recibe un entero, la segunda (L.2)
es una funcin genrica que recibe un objeto de tipo T. Ms tarde, en el cdigo podramos hacer:
int i = 128;
128

// una "instancia" de la clase de los enteros de valor

foo (i)

// invocacin de foo de L.1

A a;

// una instancia de la clase A

foo (a);

// invocacin de una versin para tipos A de foo de L.2

B b;

// una instancia de la clase B

foo (b);

// invocacin de una versin para tipos B de foo de L.2

Punto importante a entender aqu es que en realidad, foo(a) y foo(b) son invocaciones a
funciones distintas, fabricadas por el compilador a partir de la definicin (plantilla) L.2

Para ilustrarlo intentaremos una analoga: si la clase Helado-de-Fresa representara todos los
helados de fresa, de los que las "instancias" concretas seran distintos tamaos y formatos de
helados de este sabor, una plantilla Helado-de-<tipo> sera capaz de generar las clases Heladode-fresa; Helado-de-vainilla; Helado-de-chocolate, Etc. con solo cambiar adecuadamente el
argumento <tipo>. En realidad respondera al concepto genrico de "Helado-de". Las instancias
concretas de la plantilla forman una familia de productos relacionados (helados de diversos
sabores). Forzando al mximo la analoga diramos "especialidades".
Advertiremos desde ahora que el mecanismo de plantillas C++ es en realidad un generador de
cdigo parametrizado. La conjuncin de ambas capacidades: generar tipos (datos) y cdigo
(algoritmos) les confiere una extraordinaria potencia. Si bien el propio inventor del lenguaje
reconoce que a costa de "cierta complejidad", debida principalmente a la variedad de contextos en
los que las plantillas pueden ser definidas y utilizadas [3].

La idea central a resaltar aqu es que una plantilla genera la definicin de una clase o de una
funcin mediante uno o varios parmetros. A esta instancia concreta de la clase o funcin
generada, se la denomina especializacin o especialidad de la plantilla.
Nota: un aspecto crucial del sistema es que los parmetros de la plantilla pueden ser a su vez
plantillas.

Para manejar estos conceptos utilizaremos la siguiente terminologa:


Clase-plantilla ("template class") o su equivalente: clase genrica.
Funcin-plantilla ("template function") o su equivalente: funcin genrica.

Instanciacin de la
plantilla
Clase genrica +
argumento/s

clase
concreta (especializacin)

Funcin genrica +
argumento/s

funcin
concreta (especializacin)

Como se ha indicado, las plantillas representan una de las ltimas implementaciones del lenguaje y
constituyen una de las soluciones adoptadas por C++ para dar soporte a la programacin genrica.
Aunque inicialmente fueron introducidas para dar soporte a las tcnicas que se necesitaban para la
Librera Estndar (para lo que se mostraron muy adecuadas), son tambin oportunas para muchas
situaciones de programacin. Precisamente la exigencia fundamental de diseo de la citada librera
era lograr algoritmos con el mayor grado de abstraccin posible, de forma que pudieran adaptarse
al mayor nmero de situaciones concretas.
El tiempo ha demostrado que sus autores realizaron un magnfico trabajo que va ms all de la
potencia, capacidad y versatilidad de la Librera Estndar C++ (STL
5.1) y de que otros
lenguajes hayan seguido la senda marcada por C++ en este sentido. Por ejemplo Java, con su JGL
("Java Generic Library"). Lo que comenz como una herramienta para la generacin parametrizada
de nuevos tipos de datos (clases), se ha convertido por propio derecho en un nuevo paradigma,
la metaprogramacin (programas que escriben programas).

4 Versiones explcitas e implcitas


De lo dicho hasta ahora puede deducirse que las funciones y clases obtenidas a partir de versiones
genricas (plantillas), pueden obtenerse tambin mediante codificacin manual (en realidad no se
diferencian en nada de estas ltimas). Aunque en lo tocante a eficacia y tamao del cdigo, las
primeras puedan competir en igualdad de condiciones con las obtenidas manualmente. Esto se
consigue porque el uso de plantillas no implica ningn mecanismo de tiempo de ejecucin
(runtime). Las plantillas dependen exclusivamente de las propiedades de los tipos que utiliza como
parmetros y todo se resuelve en tiempo de compilacin [4].
No existe inconveniente para la coexistencia en un programa de ambos tipos de cdigo; el
generado automticamente por el mecanismo de plantillas y el generado de forma manual. Nos
referiremos a ellos como especialidades generadas automticamente, y generadas por el usuario;
tambin como versiones explcitas (codificadas manualmente) e implcitas (generadas por el
compilador). Veremos que para ciertos efectos el compilador puede distinguir entre unas y otras (
4.12.1b).
Las plantillas representan un mtodo muy eficaz de generar cdigo (definiciones de funciones y
clases
) a partir de definiciones relativamente pequeas. Adems su utilizacin permite tcnicas
de programacin avanzadas, en las que implementaciones muy sofisticadas se muestran mediante
interfaces que ocultan al usuario la complejidad, mostrndola solo en la medida en que necesite
hacer uso de ella. De hecho, cada una de las potentes abstracciones que se utilizan en la Librera
Estndar est representada como una plantilla. A excepcin de algunas pocas funciones,
prcticamente el 100% de la Librera Estndar est relacionada con las plantillas.

5 template (palabra-clave)

C++ utiliza una palabra clave especfica template para declarar y definir funciones y clases
genricas. En estos casos acta como un especificador de tipo y va unido al par de ngulos <
> que delimitan los argumentos de la plantilla:
template <T> void fun(T& ref);
template <T> class C {/*...*/};

// declaracin de funcin genrica


// declaracin de clase genrica

En algunas otras (raras) ocasiones, la palabra template se utiliza como calificador para indicar que
determinada entidad es una plantilla (y en consecuencia puede aceptar argumentos) cuando el
compilador no puede deducirlo por s mismo. Ver ejemplo (
5.1.1e1):
bs2.template to_string <char, char_traits<char>, allocator<char> >();
En este caso se trata de la instanciacin explcita de una funcin genrica (mtodo de
clase) to_string que no acepta argumentos.

6 Webografa
Generic Programming Techniques.

4.12.1 Funciones genricas


1 Sinopsis
Hemos indicado (
1.12) que las plantillas-funcin o funciones genricas son un mecanismo
C++ que permite definir una funcin mediante uno o varios parmetros. A partir de estas plantillas,
el compilador es capaz de generar cdigo de funciones distintas que comparten ciertas
caractersticas. Las funciones as generadas se denominan instancias o especializaciones de la
plantilla. Tambin versiones implcitas, para distinguirlas de las versiones codificadas
manualmente (versiones explcitas). Este mecanismo resulta esencial para la creacin de
algoritmos genricos como los utilizados en la STL (
5.1), donde las funciones genricas son
utilizadas extensivamente como miembros de clases (en especial como constructores
parametrizados).
Para ilustrar grficamente su utilidad utilizaremos un ejemplo clsico: queremos construir una
funcin max(a, b) que pueda utilizarse para obtener el mayor de dos valores, suponiendo que
estos sean de cualquier tipo capaz de ser ordenado, es decir, cualquier tipo en el que se pueda
establecer un criterio de ordenacin (establecemos a > b. si a est despus que b en el orden).
El problema que presenta C++ para esta propuesta es que, al ser un lenguaje fuertemente tipado (
2.2), la declaracin c max(a, b) requiere especificar el tipo de argumentos y valor devuelto. En
realidad se requiere algo as:
tipoT max(tipoT a, tipoT b);
y la sintaxis del lenguaje no permite que tipoT sea una variable. Una posible solucin es
sobrecargar la funcin max(), definiendo tantas versiones como tipos distintos debamos utilizar.

Otra alternativa sera utilizar una macro:


#define max(a, b) ((a > b) ? a : b)
pero hemos sealado que esto presenta sus inconvenientes (
4.9.10b). Empezando porque su
utilizacin permitira comparar un entero con una estructura o una matriz, algo que est claramente
fuera del propsito de la funcin que pretendemos [1].

2 Funciones genricas
La solucin al problema enunciado es utilizar una funcin genrica (plantilla). La sintaxis de su
definicin es la siguiente:
template <class T> T max(T a, T b) {
return (a > b) ? a : b;
template es un especificador de tipo, e indica que se trata de una plantilla (es una palabra
clave C++)
<class T> es la lista de parmetros; representa el/los parmetros de-la-plantilla (el tipo de
dato
). Los parmetros de una plantilla funciona en cierta forma como los argumentos de una
macro (el trabajo de esta macro es generar cdigo de funciones).
Es importante significar que utilizamos dos conceptos distintos, aunque relacionados:
los parmetros de-la-plantilla (contenidos en la lista template <....> ) y
los argumentos de-la-funcin (argumentos con que se invoca la funcin en cada caso
concreto).
Los parmetros de la plantilla pueden ser tipos concretos, valores constantes o plantillas (ver la
gramtica
). Lo mismo que en las funciones explcitas, las genricas pueden ser declaradas
antes de su utilizacin:
template <class T> T max(T, T);
y definidas despus:
template <class T> T max(T a, T b) {
return (a > b) ? a : b;
}
ver a este respecto "Las plantillas y la organizacin del cdigo" (
4.12.1b). En la pgina adjunta
se muestra la gramtica C++ para este especificador (
Gramtica). Recuerde que la definicin
de una plantilla comienza siempre con template <...>
La ida fundamental es que el compilador deduce los tipos concretos de los parmetros de-laplantilla de la inspeccin de los argumentos actuales utilizados en la invocacin [1a]. Por ejemplo,
la plantilla anterior puede ser utilizada mediante las siguientes sentencias:
int i, j;
UnaClase a, b;

...
int k = max(i, j);
UnaClase c = max(a, b);

// L1
// L2

En L1 los argumentos de-la-funcin son dos objetos tipo int; mientras en L2 son dos objetos
tipo UnaClase. El compilador es capaz de construir dos funciones aplicando los parmetros
adecuados a-la-plantilla. En el primer caso, el parmetro es un int; en el segundo un
tipo UnaClase.
Veremos ms adelante
, que es de la mxima importancia que el compilador
sea capaz de deducir los parmetros de-la-plantilla a partir de los argumentos actuales (los
utilizados en cada invocacin de la funcin). Veremos tambin las medidas sintcticas adoptadas
cuando tal deduccin no es posible.
Observe que en este tipo de funciones, los argumentos pueden ser objetos muy complejos
cuya construccin puede ser costosa, razn por la que es preferible pasar los argumentos por
referencia antes que por valor (
4.4.5). As, la declaracin de la funcin anterior estara mejor en
la forma:
template <class T> T max(T&, T&);

2.1 Una funcin genrica puede tener ms argumentos que la plantilla. Por ejemplo:
template <class T> void func(T, inf, char, long, ...);
Tambin puede tener menos:
template <class T> void func();
Nota: mas adelante se muestra la forma de operar en este caso, para que el compilador
deduzca el parmetro correcto T a utilizar en la plantilla
.

2.2 La gramtica muestra que el especificador class de la lista de parmetros puede ser
sustituido por typename (
3.2.1e), de forma que la expresin anterior equivale a:
template <typename T> T max(T a, T b) {
return (a > b) ? a : b;
}

3 Observaciones
Llegados a este punto algunas observaciones importantes:

3.1 Las funciones genricas son entes de nivel de abstraccin superior a las funciones
concretas (en este contexto preferimos llamarlas funciones explcitas), pero las funciones genricas
solo tienen existencia en el cdigo fuente y en la mente del programador. Hemos dicho que el
mecanismo de plantillas C++ se resuelve en tiempo de compilacin, de modo que en el ejecutable,
y durante la ejecucin, no existe nada parecido a una funcin genrica; solo existen
especializaciones (instancias de la funcin genrica).

Esta caracterstica de las funciones genricas es de la mayor importancia. Supone que pueden
escribirse algoritmos muy genricos en los que los detalles dependen del tipo de objeto con el que
se utiliza (el algoritmo). En nuestro ejemplo, el criterio que define que objeto a o b es mayor, no
est contenido en la funcin max(), sino en la propia clase a que pertenecen ambos objetos; en
cmo se ha definido el operador > para ese tipo concreto. Esta es justamente la premisa
fundamental de la programacin genrica (
4.12)

3.2 Instanciacin
Los parmetros de la plantilla introducen ciertos identificadores (nombres como el T de los
ejemplos anteriores) en el mbito de la definicin de esta, que pueden referirse a tipos; constantes
u otras plantillas (a su vez, los tipos pueden ser bsicos; extendidos o compuestos
2.2a).
Cuando la plantilla es instanciada, los identificadores son sustituidos por tipos o constantes
concretas y el cdigo es compilado (cuando los parmetros son a su vez plantillas, los
identificadores involucrados son sustituidos en un proceso recursivo hasta que todos han sido
sustituidos por tipos o constantes concretos).
La instanciacin de la plantilla se produce cuando el compilador necesita una versin concreta
(especialidad) de la funcin genrica, lo que sucede cuando encuentra una invocacin como
L.2
, o se toma la direccin de la funcin (por ejemplo para iniciar un puntero-a-funcin).
Entonces se genera el cdigo apropiado en concordancia con el tipo de argumentos actuales [3].
Ejemplo:
int m, m;
UnaClase a, b;
...
int j = max(m, n);
UnaClase obj = max(a, b);

// L1: versin para enteros


// L2: versin para objetos UnaClase

En este caso, al llegar a L1 el compilador genera el cdigo de una funcin que acepta dos int, e
incluye en este punto una invocacin a la direccin correspondiente con los parmetros
adecuados. En L2 las cosas ocurren de forma anloga. El compilador genera el cdigo para una
funcin que acepte dos objetos tipo UnaClase, e inserta el cdigo para la invocacin pertinente.

Ocurre que si esta instancia aparece ms de una vez en un mdulo, o es generada en ms de un


mdulo, el enlazador las refunde automticamente en una sola definicin, de forma que solo exista
una copia de cada instancia. Dicho en otras palabras: en la aplicacin resultante solo existir una
definicin de cada funcin. Por contra, si no existe ninguna invocacin no se genera ningn cdigo
[6].
Aunque la utilizacin de funciones genricas conduce a un cdigo elegante y reducido, que no se
corresponde con el resultado final en el ejecutable. Si la aplicacin utiliza muchas plantillas con
muchos tipos diferentes, el resultado es la generacin de gran cantidad de cdigo con el
consiguiente consumo de espacio. Esta crecimiento del cdigo es conocida como "Code bloat", y
puede llegar a ser un problema. En especial cuando se utilizan las plantillas de la Librera Estndar
(
5.1), aunque existen ciertas tcnicas para evitarlo. Como regla general, las aplicaciones que
hace uso extensivo de plantillas resultan grandes consumidoras de memoria (es el costo de la
comodidad).

La unificacin de las definiciones que aparezcan en ms de un mdulo, depende naturalmente de


la capacidad de optimizacin del compilador, pero en general, el problema de las funciones
genricas es el "code bloat". La contrapartida es que resultan funciones rpidas (tanto como la
versin explcita equivalente).
Debido a que las funciones genricas permiten cdigo fuente genrico, en cierta forma han sido
comparadas con las funciones virtuales (
4.11.8a), dado que el polimorfismo permite escribir
cdigo objeto genrico [5]. En este sentido, las funciones virtuales representaran una alternativa a
las funciones genricas. La ventaja en este caso sera la mayor compacidad del cdigo. La
contrapartida es que el "Late bindign" de las funciones virtuales las hace comparativamente ms
lentas.

Suponiendo para la plantilla max(a, b) la definicin anterior (2


), el compilador se preocupa de
sustituir la expresin a > b por la apropiada invocacin de la funcin a.operator>(b) (
4.9.18). Lo que significa que la funcin puede emplearse con cualquier tipo para el que se haya
definido la funcin-operador correspondiente (
4.9.18b1).
Puesto que los tipos bsicos disponen de sus propias versiones (globales) de las funcionesoperador (
4.9.18), una plantilla como 2 , puede ser utilizada no solo con tipos complejos,
tambin con los tipos fundamentales.
Puesto que cada instancia de una funcin genrica es una verdadera funcin, cada especializacin
dispone de su propia copia de las variables estticas locales que hubiese. Se les pueden declarar
punteros y en general gozan de todas las propiedades de las funciones normales, incluyendo la
capacidad de sobrecarga (
4.12.1a).

4 Ejemplo
Veamos un caso concreto con una funcin genrica que utiliza la clase Vector a la que ya nos
hemos referido en captulos anteriores [2]:
#include <iostream>
using namespace std;
class Vector {
public: float x, y;
bool operator>(Vector v) {
// L6 operador > para la clase
return ((x * x + y * y) > (v.x * v.x + v.y * v.y))? true: false;
}
};
template<class T> T max(T a, T b) { return (a > b) ? a : b; };
// L10
void main() {
// =====================
Vector v1 = {2, 3}, v2 = {1, 5};
int x = 2, y = 3;
cout << "Mayor: " << max(x, y) << endl;
// M3
cout << "Mayor: " << max(v1, v2).x << ", " << max(v1,v2).y << endl; //
M4
}
Salida:

Mayor: 3
Mayor: 1, 5
Comentario
En L6 se ha definido una versin sobrecargada del operador binario > para los miembros de la
clase. En L10 se define la funcin genrica (plantilla)
T max(T, T).
Puede comprobarse que el compilador ha generado, y utilizado, correctamente el cdigo de las
funciones max(int, int) para la invocacin de M3, y max(Vector, Vector) para M4.
Observe que la utilizacin de la funcin genrica max con objetos de cualquier clase C, exige que
en la definicin de la clase est sobrecargado el operador >. Es decir, debe existir una funcinoperador del siguiente aspecto:
bool operator>(C c) {
...;
}

// operador > para la clase

5 Mtodos genricos
Las funciones genricas pueden ser miembros (mtodos) de clases:
class A {
template<class T> void func(T& t) { // definicin de mtodo genrico
...
}
...
}
La definicin de mtodos genricos puede hacerse tambin fuera del cuerpo de la clase (off-line):
class A {
template<class T> void func(T& t); // declaracin de mtodo genrico
...
}
...
template <class T> void A::func(T& t) { // definicin off-line
...
}
Ver ejemplo ejecutable de un mtodo genrico en una clase (

Ejemplo)

Los miembros genricos pueden ser a su vez miembros de clases genricas (

4.12.2):

template<class X> class A {


// clase genrica
template<class T> void func(T& t); // mtodo genrico de clase genrica
...
}

6 Parmetros de-la-plantilla
La definicin de la funcin genrica puede incluir ms de un argumento. Es decir, el
especificador template <...> puede contener una lista con varios tipos. Estos parmetros
pueden ser tipos complejos o fundamentales (
2.2), por ejemplo un int; incluso
especializaciones de clases genricas (
4.12.2) y constantes de tiempo de compilacin. Por
contra, las funciones genricas no pueden ser argumentos.
template <class A, class B> void func(A, B);
tamplate <class A, func(A, B)> void f(A, B);
// Error!!
template <class A, int x> void func(A, int);
template <class A, size_t N> void func(A, N);
template<class T> class X {...};
// una clase genrica
template<class A, class T> void func(A, X<T>);

6.1
A diferencia de las clases genricas, los argumentos de las funciones
genricas no pueden tener valores por defecto. Lo mismo que en las funciones explcitas, en las
funciones genricas debe existir concordancia entre el nmero y tipo de los argumentos formales y
actuales.
template <class A, int x> void func(A, int);
...
func(T);
// Error!! falta 1 argumento

6.2 Evitar indefiniciones


Todos los argumentos formales de-la-plantilla (contenidos en la lista template <....> ) deben estar
representados en los argumentos formales de la funcin. De no ser as, no habra posibilidad de
deducir los valores de los tipos de la lista <....> cuando se produzca una invocacin especfica de
la funcin.
Ejemplo:
template <class A, class B> void func(A a) { // Plantilla
...
};
...
func(a);
// Error de compilacin !!
En este caso el compilador no tiene posibilidad de deducir el tipo del argumento B de-la-plantilla.
Por ejemplo, en MS VC++ es el error C2783:'declaration': could not deduce template
argument for 'identifier'.

6.3 Especificacin explcita de parmetros


En ocasiones el diseo de la funcin no permite determinar el tipo de parmetro de-la-plantilla a
partir de los argumentos actuales de la invocacin (o sencillamente se quiere obligar al compilador
a aplicar los argumentos actuales a una especializacin instanciada con unos parmetros
concretos). Por ejemplo:

templete <class T> T* build() {


// funcin genrica
try { return T* = new T; }
catch (bad_alloc) {
cout << "Memoria agotada" << endl;
abort();
}
}
La plantilla anterior crea un objeto de cualquier tipo y devuelve un puntero al objeto creado, o
aborta el programa en caso de no haber memoria suficiente, pero el compilador no puede deducir
el tipo de parmetro a utilizar con la plantilla a partir del argumento actual:
int* iptr = build();

// Error parmetro T desconocido

Para su utilizacin debe especificarse explcitamente el tipo de parmetro a utilizar mediante un


argumento:
int* iptr = build<int>(); // Ok. T es int

La gramtica del lenguaje exige que cuando el argumento de la plantilla solo es utilizado por la
funcin en el tipo de retorno, debe indicarse explcitamente que tipo de instanciacin se pretende
mediante la inclusin de parmetros de plantilla <...> entre el nombre de la funcin y la lista de
argumentos: foo <...> (...).
Esta forma se denomina instanciacin implcita especfica [4], y en estos casos la lista <...> que
sigue al nombre de la funcin genrica puede incluir los parmetros de la plantilla que sean
necesarios. Esta lista no tiene porqu incluir a todos los parmetros actualmente utilizados por la
plantilla, ya que el compilador la completa con los tipos que puedan deducirse de la lista de
argumentos de la funcin. Sin embargo, los parmetros faltantes deben ser los ltimos de la
lista <...> (anlogo a lo exigido a los argumentos por defecto en las funciones explcitas
4.4.5).
Ejemplo:
template <class A, class
....
func(b, c, i);
func <A, B, C>(b, c, i);
func<A>(b, c, i);
func<A>(b, c);

B, class C> void func(B b, C c, int i);


//
//
//
//

Error!!
Ok. B y C redundantes
Ok.
Error!! falta argumento i

En ocasiones los parmetros de plantilla ayudan a determinar el tipo de instanciacin a utilizar en


cada caso. Por ejemplo, hemos visto que la funcin genrica
template <typename T> T max(T a, T b) {
return (a > b) ? a : b;
}
Permite utilizarla en invocaciones del tipo:

cout << max(5, 10) << endl;


cout << max('A', 'B') << endl;

// -> 10
// -> B

Pero si queremos utilizar distintos tipos en una misma invocacin necesitamos los parmetros de
plantilla para instruir al compilador sobre el tipo a utilizar [7]:
cout << max<int>(5, 'B') << endl;
cout << max<char>(5, 'B') << endl;

// -> 66
// -> B

La Librera Estndar ofrece algunos ejemplos de funciones genricas que precisan de instanciacin
implcita especfica. Por ejemplo las funcioneshas_facet(
5.2.1) y use_facet(
5.2.1)
Nota: la posibilidad de especificar explcitamente los parmetros, permite implementar un pseudooperador
implicit_cast<T>(arg)
que puede funcionar de forma semejante a los operadores C++ de modelado (

implicit_cast)

6.4 Evitar ambigedades


Como sealbamos al principio, un aspecto crucial de las funciones genricas es que el
compilador debe poder deducir sin ambigedad los argumentos de-la-plantilla a partir de los
argumentos utilizados para la invocacin de la funcin (argumentos actuales
4.4.5).
Recordemos que en los casos de sobrecarga, la invocacin de funciones C++ utiliza un sistema
estndar para encontrar la definicin que mejor se adapta a los argumentos actuales. Tambin se
realizan transformaciones automticas cuando los argumentos pasados a la funcin no concuerdan
exactamente con los esperados (argumentos formales). Estos mecanismos utilizan unas reglas
denominadas congruencia estndar de argumentos (
4.4.1a).
En caso de funciones genricas, el compilador deduce los parmetros de-plantilla mediante el
anlisis de los argumentos actuales de-la-invocacin, pero para esto solo realiza conversiones
triviales (menos significativas que las realizadas con las funciones explcitas).
Ejemplo:
template<class T> bool igual(T a, T b){ // funcin genrica
return (a == b) ? true : false;
}
bool desigual(double a, double b){
// funcin explcita
return (a == b) ? false : true;
}
int i;
char c;
double d;
...
igual(i, i);
// Ok. invoca igual(int ,int)
igual(c, c);
// Ok. invoca igual(char,char)
igual(i, c);
// Error!! igual(int,char) indefinida
igual(c, i);
// Error!! igual(char,int) indefinida

desigual(i,
desigual(c,
desigual(i,
desigual(d,

i)
c)
c)
d)

//
//
//
//

Ok.
Ok.
Ok.
Ok.

conversin de argumentos efectuada


conversin de argumentos efectuada
conversin de argumentos efectuada
concordancia de argumentos

6.4.1 Lo anterior no es vlido para funciones-miembro de clases genricas (


4.12.2) ya que
estas pueden deducir la identidad de sus miembros por el contexto. Por ejemplo:
template <class A, class B> class ClasA {
// clase genrica
a...; b...;
void func();
// funcin-miembro
};
...
template <class A, class B> void ClasA<A, B>::func() { // definicin offline
a...; b...;
}

6.5 Deben evitarse ambigedades que dificulten al compilador la identificacin de los tipos a
utilizar en la plantilla. Por ejemplo:
template<class T> class Cx {...};
template<class T> void func(Cx<T>);
...
func(1);

// clase genrica
// funcin genrica
// Error!! invocacin

En estas circunstancias el compilador produce un error, ya que es incapaz de deducir el


argumento T de la plantilla a partir del argumento actual utilizado en la invocacin de la funcin. En
cambio, el siguiente cdigo resuelve la ambigedad:
Cx<int> x = 1;
func(x);
// Ok. x es tipo Cx<int>

T es int

Nota: en el captulo dedicado a las clases genricas se incluyen ms detalles sobre los
argumentos del especificador template ( 4.12.2).

ms fcil entender los problemas que pueden

Anda mungkin juga menyukai