plantillas en C++Edit

Artículo principal: Template (C++)

C++ utiliza plantillas para habilitar técnicas de programación genéricas. La biblioteca estándar de C++ incluye la Biblioteca de plantillas estándar o STL que proporciona un marco de plantillas para estructuras de datos y algoritmos comunes. Las plantillas en C++ también se pueden usar para la metaprogramación de plantillas, que es una forma de pre-evaluar parte del código en tiempo de compilación en lugar de en tiempo de ejecución. Usando la especialización de plantillas, las plantillas de C++ se consideran Turing completas.,

Technical overviewEdit

Hay dos tipos de plantillas: plantillas de función y plantillas de clase. Una plantilla de función es un patrón para crear funciones ordinarias basadas en los tipos de parametrización proporcionados cuando se instancian. Por ejemplo, la Biblioteca de plantillas estándar de C++ contiene la plantilla de función max (x, y) que crea funciones que devuelven x O y, la que sea mayor., max() se puede definir así:

template <typename T>T max(T x, T y) { return x < y ? y : x;}

las especializaciones de esta plantilla de función, las instancias con tipos específicos, se pueden llamar como una función ordinaria:

std::cout << max(3, 7); // Outputs 7.

el compilador examina los argumentos utilizados para llamar a Max y determina que se trata de una llamada a max(Int, Int)., A continuación, crea una instancia de una versión de la función donde el tipo de parametrización T es int, haciendo el equivalente de la siguiente función:

int max(int x, int y) { return x < y ? y : x;}

esto funciona si los argumentos x E y son enteros, cadenas, o cualquier otro tipo para el que la expresión x < y es sensible, o más específicamente, para cualquier tipo el operador< está definido. La herencia común no es necesaria para el conjunto de tipos que se pueden usar, por lo que es muy similar a duck typing., Un programa que define un tipo de datos personalizado puede usar la sobrecarga del operador para definir el significado de < para ese tipo, permitiendo así su uso con la plantilla de función max (). Si bien esto puede parecer un beneficio menor en este ejemplo aislado, en el contexto de una biblioteca completa como la STL, permite al programador obtener una funcionalidad extensa para un nuevo tipo de datos, simplemente definiendo algunos operadores para él., El mero hecho de definir < permite que un tipo se use con los algoritmos estándar sort(), stable_sort () y binary_search() o que se coloque dentro de estructuras de datos como conjuntos, montones y matrices asociativas.

Las plantillas de c++ son completamente seguras en tiempo de compilación. Como demostración, el tipo estándar complejo no define el operador <, porque no hay un orden estricto en los números complejos. Por lo tanto, max(x, y) fallará con un error de compilación, si x E y son valores complejos., Del mismo modo, otras plantillas que dependen de < no se pueden aplicar a datos complejos a menos que se proporcione una comparación (en forma de un funtor o función). Por ejemplo: un complejo no se puede usar como clave para un mapa a menos que se proporcione una comparación. Desafortunadamente, los compiladores históricamente generan mensajes de error algo Esotéricos, largos e inútiles para este tipo de error. Asegurarse de que un determinado objeto se adhiere a un protocolo de método puede aliviar este problema. Los idiomas que usan comparar en lugar de < también pueden usar valores complejos como claves.,

el segundo tipo de plantilla, una plantilla de clase, extiende el mismo concepto a las clases. Una especialización de plantilla de clase es una clase. Las plantillas de clase se utilizan a menudo para crear contenedores genéricos. Por ejemplo, la STL tiene un contenedor de lista vinculada. Hacer una lista enlazada de números enteros, uno escribe list<int>. Una lista de cadenas se denota lista<string>. Una lista tiene un conjunto de funciones estándar asociadas a ella, que funcionan para cualquier tipo de parametrización compatible.,

Especializacióneditar

una característica poderosa de las plantillas de c++es la especialización de plantillas. Esto permite que se proporcionen implementaciones alternativas basadas en ciertas características del tipo parametrizado que se está instanciando. La especialización de plantillas tiene dos propósitos: permitir ciertas formas de optimización y reducir la saturación de código.

por ejemplo, considere una función de plantilla sort (). Una de las actividades principales que hace una función de este tipo es intercambiar o intercambiar los valores en dos de las posiciones del contenedor., Si los valores son grandes (en términos del número de bytes que se necesita para almacenar cada uno de ellos), entonces a menudo es más rápido construir primero una lista separada de punteros a los objetos, Ordenar esos punteros y luego construir la secuencia ordenada final. Sin embargo, si los valores son bastante pequeños, generalmente es más rápido cambiar los valores en el lugar según sea necesario. Además, si el tipo parametrizado ya es de algún tipo de puntero, entonces no hay necesidad de construir una matriz de puntero separada., La especialización de plantillas permite al creador de plantillas escribir diferentes implementaciones y especificar las características que deben tener los tipos parametrizados para cada implementación que se va a utilizar.

a diferencia de las plantillas de función, las plantillas de clase Pueden ser parcialmente especializadas. Esto significa que se puede proporcionar una versión alternativa del código de plantilla de clase cuando se conocen algunos de los parámetros de la plantilla, mientras que otros parámetros de la plantilla son genéricos., Esto se puede usar, por ejemplo, para crear una implementación predeterminada (la especialización primaria) que asume que copiar un tipo de parametrización es costoso y luego crear especializaciones parciales para tipos que son baratos de copiar, aumentando así la eficiencia general. Los clientes de tal plantilla de clase solo usan especializaciones de ella sin necesidad de saber si el compilador usó la especialización primaria o alguna especialización parcial en cada caso., Las plantillas de clase también pueden ser completamente especializadas, lo que significa que se puede proporcionar una implementación alternativa cuando se conocen todos los tipos de parametrización.

ventajas y desventajaseditar

algunos usos de las plantillas, como la función max (), se llenaban previamente con macros de preprocesador Tipo Función (un legado del lenguaje de programación C). Por ejemplo, aquí hay una posible macro max ():

#define max(a,b) ((a) < (b) ? (b) : (a))

Las Macros se expanden por preprocesador, antes de la compilación propiamente dicha; las plantillas se expanden en tiempo de compilación., Las Macros siempre se expanden en línea; las plantillas también se pueden expandir como funciones en línea cuando el compilador lo considere apropiado. Por lo tanto, tanto las macros como las plantillas de funciones no tienen sobrecarga en tiempo de ejecución.

sin embargo, las plantillas generalmente se consideran una mejora sobre las macros para estos fines. Las plantillas son seguras para el tipo. Las plantillas evitan algunos de los errores comunes que se encuentran en el código que hace un uso intensivo de macros similares a funciones, como evaluar parámetros con efectos secundarios dos veces. Quizás lo más importante, las plantillas fueron diseñadas para ser aplicables a problemas mucho más grandes que las macros.,

Hay cuatro inconvenientes principales en el uso de plantillas: características soportadas, compatibilidad con compiladores, mensajes de error deficientes y exceso de código:

  1. Las plantillas en C++ carecen de muchas características, lo que hace que implementarlas y usarlas de una manera directa a menudo sea imposible. En su lugar, los programadores tienen que confiar en trucos complicados que conducen a un código hinchado, difícil de entender y difícil de mantener. Los desarrollos actuales en los estándares de C++ exacerban este problema al hacer un uso intensivo de estos trucos y construir muchas características nuevas para plantillas en ellos o con ellos en mente.,
  2. muchos compiladores históricamente tienen un soporte pobre para las plantillas, por lo que el uso de plantillas puede hacer que el código sea algo menos portátil. El soporte también puede ser pobre cuando se usa un compilador de C++ con un enlazador que no es compatible con C++, o cuando se intenta usar plantillas a través de los límites de bibliotecas compartidas. Sin embargo, la mayoría de los compiladores modernos ahora tienen un soporte de plantilla bastante robusto y estándar, y el nuevo estándar de C++, C++11, aborda estos problemas.
  3. casi todos los compiladores producen mensajes de error confusos, largos o, a veces, inútiles cuando se detectan errores en el código que utiliza plantillas., Esto puede hacer que las plantillas sean difíciles de desarrollar.
  4. Finalmente, el uso de plantillas requiere que el compilador genere una instancia separada de la clase o función templada para cada permutación de los parámetros de tipo utilizados con ella. (Esto es necesario porque los tipos en C++ no son todos del mismo tamaño, y los tamaños de los campos de datos son importantes para el funcionamiento de las clases.) Por lo que el uso indiscriminado de plantillas puede llevar a la saturación de código, lo que resulta en ejecutables excesivamente grandes., Sin embargo, el uso juicioso de la especialización y derivación de plantillas puede reducir drásticamente dicha saturación de código en algunos casos:

por lo tanto, ¿se puede usar la derivación para reducir el problema de código replicado porque se utilizan plantillas? Esto implicaría derivar una plantilla de una clase ordinaria. Esta técnica demostró tener éxito en la reducción de la hinchazón de código en el uso real. Las personas que no utilizan una técnica como esta Han descubierto que el código replicado puede costar megabytes de espacio de código incluso en programas de tamaño moderado.,

iv— – Bjarne Stroustrup, the Design and Evolution of C++, 1994

las instancias adicionales generadas por las plantillas también pueden hacer que los depuradores tengan dificultades para trabajar con las plantillas. Por ejemplo, establecer un punto de interrupción de depuración dentro de una plantilla desde un archivo de origen puede omitir establecer el punto de interrupción en la instanciación real deseada o puede establecer un punto de interrupción en cada lugar en el que se instancie la plantilla.,

Además, debido a que el compilador necesita realizar expansiones macro de plantillas y generar diferentes instancias de ellas en tiempo de compilación, el código fuente de implementación para la clase o función templada debe estar disponible (por ejemplo, incluido en un encabezado) para el código que lo usa. Las clases o funciones templadas, incluyendo gran parte de la Biblioteca de plantillas estándar (STL), si no se incluyen en los archivos de cabecera, no se pueden compilar. (Esto está en contraste con el código no templado, que puede ser compilado en binario, proporcionando solo un archivo de encabezado de declaraciones para el código que lo usa.,) Esto puede ser una desventaja al exponer el código de implementación, que elimina algunas abstracciones, y podría restringir su uso en proyectos de código cerrado.

plantillas en DEdit

El lenguaje de programación D admite plantillas basadas en diseño en C++.,La mayoría de los modismos de plantilla de c++ se transferirán a D sin alteración, pero d agrega algunas funcionalidades adicionales:

  • Los parámetros de plantilla en D no están restringidos a solo tipos y valores primitivos, sino que también permiten valores arbitrarios en tiempo de compilación (como cadenas y literales de estructuras), y alias a identificadores arbitrarios, incluidas otras plantillas o instanciaciones de plantilla.
  • Las restricciones de plantilla y la instrucción estática if proporcionan una alternativa al mecanismo de sustitución de C++failure is not an error (SFINAE), similar a los conceptos de C++.
  • El es(…,) expression permite la instanciación especulativa para verificar los rasgos de un objeto en tiempo de compilación.
  • La Palabra clave auto y la expresión typeof permiten la inferencia de tipos para declaraciones de variables y valores de retorno de funciones, lo que a su vez permite «tipos Voldemort» (tipos que no tienen un nombre global).

Las plantillas en D usan una sintaxis diferente a la de C++: mientras que en C++ los parámetros de plantilla están envueltos entre corchetes angulares (Template< param1, param2>),D usa un signo de exclamación y paréntesis: Template!(param1, param2).,Esto evita las dificultades de análisis de C++ debido a la ambigüedad con los operadores de comparación.Si solo hay un parámetro, los paréntesis se pueden omitir.

convencionalmente, d combina las características anteriores para proporcionar polimorfismo en tiempo de compilación utilizando programación genérica basada en rasgos.,Por ejemplo, un rango de entrada se define como cualquier tipo que satisface las comprobaciones realizadas por isInputRange, que se define de la siguiente manera:

una función que solo acepta rangos de entrada puede usar la plantilla anterior en una restricción de plantilla:

auto fun(Range)(Range range) if (isInputRange!Range){ // ...}
Code generationEdit

Además de la metaprogramación de plantillas, D también proporciona generación de código de tiempo:

  • La expresión de importación permite leer un archivo desde el disco y usar su contenido como una expresión de cadena.,
  • La reflexión en tiempo de compilación permite enumerar e inspeccionar las declaraciones y sus miembros durante la compilación.
  • Los atributos definidos por el usuario permiten a los usuarios adjuntar identificadores arbitrarios a las declaraciones, que luego se pueden enumerar mediante reflexión en tiempo de compilación.
  • La ejecución de funciones en tiempo de compilación (CTFE) permite interpretar un subconjunto de D (restringido a operaciones seguras) durante la compilación.
  • String mixins permiten evaluar y compilar el contenido de una expresión de cadena como código D que se convierte en parte del programa.,

combinar lo anterior permite generar código basado en declaraciones existentes.Por ejemplo, D serialization frameworks puede enumerar los miembros de un tipo y generar funciones especializadas para cada tipo serializado para realizar serialización y deserialización.Los atributos definidos por el usuario podrían indicar más reglas de serialización.

la expresión de importación y la ejecución de la función en tiempo de compilación también permiten implementar eficientemente lenguajes específicos de dominio.,Por ejemplo, dada una función que toma una cadena que contiene una plantilla HTML y devuelve un código fuente d equivalente, es posible usarlo de la siguiente manera:

Genericidad en EiffelEdit

Las clases genéricas han sido parte de Eiffel desde el método original y el diseño del lenguaje. Las publicaciones de la Fundación Eiffel, utilizan el término genericidad para describir la creación y el uso de clases genéricas.

basic/Unconstrained genericityEdit

Las clases genéricas se declaran con su nombre de clase y una lista de uno o más parámetros genéricos formales., En el siguiente código, class LIST tiene un parámetro genérico formal G

los parámetros genéricos formales son marcadores de posición para nombres de clase arbitrarios que se proporcionarán cuando se realice una declaración de la clase genérica, como se muestra en las dos derivaciones genéricas a continuación, donde ACCOUNT y DEPOSIT son otros nombres de clase. ACCOUNTy DEPOSITse consideran parámetros genéricos reales, ya que proporcionan nombres de clase reales para sustituir a G en uso real.,

 list_of_accounts: LIST -- Account list list_of_deposits: LIST -- Deposit list

dentro del sistema de tipo Eiffel, aunque class LIST se considera una clase, no se considera un tipo. Sin embargo, una derivación genérica de LIST como LIST se considera un tipo.

constrained genericityEdit

para la clase list mostrada anteriormente, un parámetro genérico real que sustituye a G puede ser cualquier otra clase disponible., Para restringir el conjunto de clases de las que se pueden elegir parámetros genéricos reales válidos, se puede especificar una restricción genérica. En la declaración de la clase SORTED_LIST a continuación, la restricción genérica dicta que cualquier parámetro genérico real válido será una clase que hereda de la clase COMPARABLE. La restricción genérica garantiza que los elementos de un SORTED_LIST puedan ser ordenados.,

class SORTED_LIST 

genéricos en JavaEdit

Artículo principal: genéricos en Java

Soporte para los genéricos, o» contenedores de tipo T » se añadió al lenguaje de programación Java en 2004 como parte de J2SE 5.0. En Java, los genéricos solo se comprueban en tiempo de compilación para la corrección de tipos. La información de tipo genérica se elimina a través de un proceso llamado borrado de Tipo, para mantener la compatibilidad con las implementaciones JVM antiguas, por lo que no está disponible en tiempo de ejecución., Por ejemplo, una lista<String> se convierte en la lista de tipos sin procesar. El compilador inserta moldes de Tipo para convertir los elementos al tipo de cadena cuando se recuperan de la lista, Lo que reduce el rendimiento en comparación con otras implementaciones como las plantillas de C++.

Genericidad en. Net Edit

Los genéricos se agregaron como parte de.NET Framework 2.0 en noviembre de 2005, basados en un prototipo de Investigación de Microsoft Research iniciado en 1999. Aunque similar a los genéricos en Java, .,Net generics No aplica borrado de tipo, pero implementa genéricos como un mecanismo de primera clase en el tiempo de ejecución mediante reificación. Esta opción de diseño proporciona funcionalidad adicional, como permitir la reflexión con la preservación de tipos genéricos, así como aliviar algunas de las limitaciones del borrado (como no poder crear matrices genéricas). Esto también significa que no hay impacto en el rendimiento de los lanzamientos en tiempo de ejecución y conversiones de boxeo normalmente costosas., Cuando los tipos primitivos y de valor se utilizan como argumentos genéricos, obtienen implementaciones especializadas, lo que permite colecciones y métodos genéricos eficientes. Al igual que en C++ y Java, los tipos genéricos anidados como Dictionary<string, List<int>> son tipos válidos, sin embargo, se desaconseja para las firmas de miembros en las reglas de diseño de análisis de código.

.,NET permite seis variedades de restricciones de tipo genérico utilizando la palabra clave where, incluyendo la restricción de tipos genéricos para ser tipos de valor, para ser clases, para tener Constructores y para implementar interfaces. A continuación se muestra un ejemplo con una restricción de interfaz:

El método MakeAtLeast() permite la operación en matrices, con elementos de tipo genérico T. La restricción de Tipo del método indica que el método es aplicable a cualquier tipo T que implemente la interfaz IComparable genérica<T>., Esto asegura un error de tiempo de compilación, si el método es llamado si el tipo no soporta comparación. La interfaz proporciona el método genérico CompareTo (T).

el método anterior también podría escribirse sin tipos genéricos, simplemente usando el tipo de matriz no Genérico. Sin embargo, dado que los arrays son contravariantes, el casting no sería seguro de tipos, y el compilador no podría encontrar ciertos errores posibles que de otra manera se detectarían al usar tipos genéricos. Además, el método necesitaría acceder a los elementos de la matriz como objetos en su lugar, y requeriría casting para comparar dos elementos., (Para tipos de valor como tipos como int esto requiere una conversión de boxeo, aunque esto se puede trabajar alrededor usando el comparador<T> clase, como se hace en las clases de colección estándar.)

un comportamiento notable de los miembros estáticos en una clase. NET genérica es la instanciación de miembros estáticos por tipo de tiempo de ejecución (ver ejemplo a continuación).

Genericidad en DelphiEdit

El dialecto Object Pascal de Delphi adquirió genéricos en la versión de Delphi 2007, inicialmente solo con el (ahora descontinuado).,Compilador de red antes de ser agregado al código nativo en la versión de Delphi 2009. La semántica y las capacidades de los genéricos de Delphi se basan en gran medida en los que tienen los genéricos EN.Net 2.0, aunque la implementación es por necesidad bastante diferente. Aquí hay una traducción más o menos directa del primer ejemplo de C# mostrado arriba:

al igual que con C#, los métodos, así como los tipos enteros, pueden tener uno o más parámetros de tipo. En el ejemplo, TArray es un tipo genérico (definido por el lenguaje) y MakeAtLeast un método genérico., Las restricciones disponibles son muy similares a las restricciones disponibles en C#: cualquier tipo de valor, cualquier clase, una clase o interfaz específica y una clase con un constructor sin parámetros. Múltiples Restricciones actúan como una unión aditiva.

Genericidad en FREE PascalEdit

Free Pascal implementó genéricos antes de Delphi, y con diferente sintaxis y semántica. Sin embargo, desde la versión 2.6.0 de FPC, la sintaxis de estilo Delphi está disponible cuando se utiliza el modo de lenguaje {mode mode Delphi}. Por lo tanto, los programadores de Free Pascal son capaces de usar genéricos en cualquier estilo que prefieran.,

Ejemplo de Delphi y Free Pascal:

Functional languagesEdit

Genericidad en HaskellEdit

el mecanismo de clase de tipo de Haskell admite programación genérica.Seis de las clases de tipo predefinidas en Haskell (incluyendo Eq, los tipos que se pueden comparar por igualdad, y Show, los tipos cuyos valores se pueden representar como cadenas) tienen la propiedad especial de soportar instancias derivadas., Esto significa que un programador que define un nuevo tipo puede declarar que este tipo debe ser una instancia de una de estas clases de tipo especiales, sin proporcionar implementaciones de los métodos de clase como suele ser necesario al declarar instancias de clase. Todos los métodos necesarios serán «derivados» – es decir, construidos automáticamente-basados en la estructura del tipo.,Por ejemplo, la siguiente declaración de un tipo de árboles binarios establece que debe ser una instancia de las clases Eq y Show:

data BinTree a = Leaf a | Node (BinTree a) a (BinTree a) deriving (Eq, Show)

esto resulta en una función de igualdad (==) y una función de representación de cadena (show) que se define automáticamente para cualquier tipo de la forma BinTree T siempre que T soporte esas operaciones.,

el soporte para instancias derivadas de Eq y Show hace que sus métodos = = y show sean genéricos de una manera cualitativamente diferente de las funciones polimórficas para-métricas: estas «funciones» (más exactamente, familias de funciones indexadas por tipos) se pueden aplicar a valores de varios tipos, y aunque se comportan de manera diferente para cada tipo de argumento, se necesita poco trabajo para agregar soporte para un nuevo tipo. Ralf Hinze (2004) ha demostrado que se puede lograr un efecto similar para las clases de tipos definidas por el usuario mediante ciertas técnicas de programación., Otros investigadores han propuesto enfoques para este y otros tipos de genericidad en el contexto de Haskell y extensiones de Haskell (discutidos a continuación).

PolyPEdit

PolyP fue la primera extensión genérica del lenguaje de programación de Haskell. En pólipos, las funciones genéricas se llaman politípicas. El lenguaje introduce una construcción especial en la que tales funciones politípicas se pueden definir a través de la inducción estructural sobre la estructura del funtor patrón de un tipo de datos regular. Los tipos de datos regulares en PolyP son un subconjunto de los tipos de datos Haskell., Un tipo de datos regular t debe ser de la clase*→*, y si a es el argumento de tipo formal en la definición, entonces todas las llamadas recursivas a t deben tener la forma T a. estas restricciones descartan tipos de datos de clase superior, así como tipos de datos anidados, donde las llamadas recursivas son de una forma diferente.La función de aplanar en PolyP se proporciona aquí como ejemplo:

Generic HaskellEdit

Generic Haskell es otra extensión de Haskell, desarrollada en la Universidad de Utrecht en los Países Bajos., Las extensiones que proporciona son:

  • Los valores indexados de tipo se definen como un valor indexado sobre los diversos constructores de tipo Haskell (unidades, tipos primitivos, sumas, Productos y constructores de tipo definidos por el usuario). Además, también podemos especificar el comportamiento de un tipo de valores indexados para un constructor específico usando casos de constructor, y reutilizar una definición genérica en otra usando casos predeterminados.

el valor indexado de tipo resultante puede ser especializado para cualquier tipo.

  • Los tipos indexados son tipos indexados sobre tipos, definidos dando un caso tanto para * como para k → k’., Las instancias se obtienen aplicando el tipo indexado a un tipo.
  • Las definiciones genéricas se pueden utilizar aplicándolas a un tipo o tipo. Esto se llama aplicación genérica. El resultado es un tipo o valor, dependiendo del tipo de definición genérica que se aplique.
  • abstracción genérica permite que las definiciones genéricas se definan abstrayendo un parámetro de tipo (de un tipo dado).
  • Los tipos indexados son tipos que se indexan sobre los constructores de tipos. Estos se pueden usar para dar tipos a valores genéricos más involucrados., Los tipos indexados de tipo resultantes pueden ser especializados para cualquier tipo.

como ejemplo, la función de igualdad en Generic Haskell:

CleanEdit

Clean ofrece pólipos basados en programación genérica y el Haskell genérico soportado por GHC> =6.0. Parametriza por tipo como esos pero ofrece sobrecarga.

otros idiomaseditar

la familia ML de lenguajes de Programación soporta programación genérica a través de polimorfismo paramétrico y módulos genéricos llamados funtores.,Tanto ml estándar como OCaml proporcionan funtores, que son similares a las plantillas de clase y a los paquetes genéricos de Ada. Las abstracciones sintácticas de Scheme también tienen una conexión con la genericidad: de hecho, son un superconjunto de plantillas à la c++.

un módulo Verilog puede tomar uno o más parámetros, a los que se asignan sus valores reales al instanciar el módulo. Un ejemplo es una matriz de registro genérica donde el ancho de la matriz se da a través de un parámetro., Tal la matriz, combinada con un vector de alambre genérico, puede hacer un búfer genérico o un módulo de memoria con un ancho de bits arbitrario a partir de una implementación de módulo único.

VHDL, derivado de Ada, también tiene capacidades genéricas.