La siguiente información no está actualizada. Debe actualizarse de acuerdo con el último borrador de Concepts Lite.
La sección 3 de la propuesta de restricciones cubre esto con una profundidad razonable.
La propuesta de conceptos se ha dejado en un segundo plano por un corto tiempo con la esperanza de que las restricciones (es decir, los conceptos básicos) se puedan desarrollar e implementar en una escala de tiempo más corta, actualmente apuntando al menos a algo en C ++ 14. La propuesta de restricciones está diseñada para actuar como una transición suave a una definición posterior de conceptos. Las restricciones son parte de la propuesta de conceptos y son un pilar necesario en su definición.
En Diseño de bibliotecas de conceptos para C ++ , Sutton y Stroustrup consideran la siguiente relación:
Conceptos = Restricciones + Axiomas
Para resumir rápidamente sus significados:
- Restricción: un predicado sobre propiedades evaluables estáticamente de un tipo. Requisitos puramente sintácticos. No es una abstracción de dominio.
- Axiomas: requisitos semánticos de tipos que se suponen verdaderos. No verificado estáticamente.
- Conceptos: requisitos generales y abstractos de los algoritmos sobre sus argumentos. Definido en términos de restricciones y axiomas.
Entonces, si agrega axiomas (propiedades semánticas) a restricciones (propiedades sintácticas), obtiene conceptos.
Concepts-Lite
La propuesta de conceptos ligeros nos trae sólo la primera parte, las limitaciones, pero este es un paso importante y necesario hacia conceptos completos.
Restricciones
Las restricciones tienen que ver con la sintaxis . Nos dan una forma de discernir estáticamente las propiedades de un tipo en tiempo de compilación, de modo que podamos restringir los tipos utilizados como argumentos de plantilla en función de sus propiedades sintácticas. En la propuesta actual de restricciones, se expresan con un subconjunto de cálculo proposicional utilizando conectivos lógicos como &&
y ||
.
Echemos un vistazo a una restricción en acción:
template <typename Cont>
requires Sortable<Cont>()
void sort(Cont& container);
Aquí estamos definiendo una plantilla de función llamada sort
. La nueva adición es la cláusula require . La cláusula require da algunas restricciones sobre los argumentos de la plantilla para esta función. En particular, esta restricción dice que el tipo Cont
debe ser un Sortable
tipo. Una cosa interesante es que se puede escribir de una forma más concisa como:
template <Sortable Cont>
void sort(Cont& container);
Ahora, si intenta pasar algo que no se considera Sortable
a esta función, obtendrá un error agradable que le indicará inmediatamente que el tipo deducido T
no es un Sortable
tipo. Si hubiera hecho esto en C ++ 11, habría tenido un error horrible desde dentro de la sort
función que no tiene sentido para nadie.
Los predicados de restricciones son muy similares a los rasgos de tipo. Toman algún tipo de argumento de plantilla y le brindan información al respecto. Las restricciones intentan responder los siguientes tipos de preguntas sobre el tipo:
- ¿Este tipo tiene tal o cual operador sobrecargado?
- ¿Se pueden utilizar estos tipos como operandos para este operador?
- ¿Este tipo tiene tal o cual rasgo?
- ¿Es esta expresión constante igual a eso? (para argumentos de plantilla que no son de tipo)
- ¿Este tipo tiene una función llamada yada-yada que devuelve ese tipo?
- ¿Este tipo cumple con todos los requisitos sintácticos para ser utilizado como tal?
Sin embargo, las restricciones no están destinadas a reemplazar los rasgos de tipo. En cambio, trabajarán de la mano. Algunos rasgos de tipo ahora se pueden definir en términos de conceptos y algunos conceptos en términos de rasgos de tipo.
Ejemplos
Entonces, lo importante de las restricciones es que no se preocupan ni un ápice por la semántica. Algunos buenos ejemplos de restricciones son:
Equality_comparable<T>
: Comprueba si el tipo tiene ==
ambos operandos del mismo tipo.
Equality_comparable<T,U>
: Comprueba si hay un ==
con operandos izquierdo y derecho de los tipos dados
Arithmetic<T>
: Comprueba si el tipo es aritmético.
Floating_point<T>
: Comprueba si el tipo es un tipo de punto flotante.
Input_iterator<T>
: Comprueba si el tipo admite las operaciones sintácticas que debe admitir un iterador de entrada.
Same<T,U>
: Comprueba si el tipo especificado es el mismo.
Puede probar todo esto con una versión básica de conceptos especiales de GCC .
Más allá de Concepts-Lite
Ahora nos adentramos en todo más allá de la propuesta de conceptos básicos. Esto es incluso más futurista que el futuro en sí. Es probable que todo de aquí en adelante cambie bastante.
Axiomas
Los axiomas tienen que ver con la semántica . Especifican relaciones, invariantes, garantías de complejidad y otras cosas similares. Veamos un ejemplo.
Si bien la Equality_comparable<T,U>
restricción le dirá que hay una operator==
que toma tipos T
y U
, no le dice qué significa esa operación . Para eso, tendremos el axioma Equivalence_relation
. Este axioma dice que cuando se comparan objetos de estos dos tipos con operator==
dar true
, estos objetos son equivalentes. Esto puede parecer redundante, pero ciertamente no lo es. Podría definir fácilmente un operator==
que, en cambio, se comportara como un operator<
. Serías malvado por hacer eso, pero podrías.
Otro ejemplo es un Greater
axioma. Está muy bien decir que dos objetos de tipo T
se pueden comparar con operadores >
y <
, pero ¿qué significan ? El Greater
axioma dice que sif x
es mayor entonces y
, entonces y
es menor que x
. La especificación propuesta, tal axioma, se ve así:
template<typename T>
axiom Greater(T x, T y) {
(x>y) == (y<x);
}
Entonces, los axiomas responden a los siguientes tipos de preguntas:
- ¿Estos dos operadores tienen esta relación entre sí?
- ¿Este operador para tal o cual tipo significa esto?
- ¿Tiene esta operación en ese tipo esta complejidad?
- ¿Este resultado de ese operador implica que esto es cierto?
Es decir, se preocupan completamente por la semántica de tipos y operaciones en esos tipos. Estas cosas no se pueden verificar estáticamente. Si es necesario verificar esto, un tipo debe proclamar de alguna manera que se adhiere a esta semántica.
Ejemplos
A continuación, se muestran algunos ejemplos comunes de axiomas:
Equivalence_relation
: Si dos objetos se comparan ==
, son equivalentes.
Greater
: Cuando sea x > y
, entonces y < x
.
Less_equal
: Cuando sea x <= y
, entonces !(y < x)
.
Copy_equality
: Para x
y y
de tipo T
: si x == y
, un nuevo objeto del mismo tipo creado por construcción de copia T{x} == y
y aún x == y
(es decir, no es destructivo).
Conceptos
Ahora los conceptos son muy fáciles de definir; son simplemente la combinación de restricciones y axiomas . Proporcionan un requisito abstracto sobre la sintaxis y la semántica de un tipo.
Como ejemplo, considere el siguiente Ordered
concepto:
concept Ordered<Regular T> {
requires constraint Less<T>;
requires axiom Strict_total_order<less<T>, T>;
requires axiom Greater<T>;
requires axiom Less_equal<T>;
requires axiom Greater_equal<T>;
}
En primer lugar, tenga en cuenta que para que el tipo de plantilla T
sea Ordered
, también debe cumplir con los requisitos del Regular
concepto. El Regular
concepto es un requisito muy básico de que el tipo se comporte bien: se puede construir, destruir, copiar y comparar.
Además de esos requisitos, se Ordered
requiere que T
cumplan una restricción y cuatro axiomas:
- Restricción: un
Ordered
tipo debe tener una extensión operator<
. Esto se comprueba estáticamente, por lo que debe existir.
- Axiomas: Para
x
y y
de tipo T
:
x < y
da un orden total estricto.
- Cuando
x
es mayor que y
, y
es menor que x
y viceversa.
- Cuando
x
es menor o igual a y
, y
no es menor que x
y viceversa.
- Cuando
x
es mayor o igual que y
, y
no es mayor que x
y viceversa.
La combinación de restricciones y axiomas como esta le da conceptos. Definen los requisitos sintácticos y semánticos de los tipos abstractos para su uso con algoritmos. Actualmente, los algoritmos deben asumir que los tipos usados admitirán ciertas operaciones y expresarán cierta semántica. Con conceptos, podremos asegurarnos de que se cumplan los requisitos.
En el último diseño de conceptos , el compilador solo verificará que el argumento de plantilla cumpla con los requisitos sintácticos de un concepto. Los axiomas no se controlan. Dado que los axiomas denotan semánticas que no son evaluables estáticamente (o, a menudo, imposibles de verificar por completo), el autor de un tipo tendría que declarar explícitamente que su tipo cumple con todos los requisitos de un concepto. Esto se conocía como mapeo conceptual en diseños anteriores, pero desde entonces se ha eliminado.
Ejemplos
A continuación se muestran algunos ejemplos de conceptos:
Regular
los tipos son construibles, destruibles, copiables y comparables.
Ordered
tipos de soporte operator<
, y tienen un orden total estricto y otra semántica de orden.
Copyable
los tipos son copiables, destruibles, y si x
es igual a y
y x
se copia, la copia también se comparará con y
.
Iterator
tipos tipos deben haber asociadas value_type
, reference
, difference_type
, y iterator_category
que a su vez deben cumplir con ciertos conceptos. También deben apoyar operator++
y ser desreferenciables.
El camino a los conceptos
Las restricciones son el primer paso hacia una función de conceptos completos de C ++. Son un paso muy importante, porque proporcionan los requisitos de tipos exigibles estáticamente para que podamos escribir funciones y clases de plantilla mucho más limpias. Ahora podemos evitar algunas de las dificultades y la fealdad de std::enable_if
y sus amigos de la metaprogramación.
Sin embargo, hay una serie de cosas que la propuesta de restricciones no hace:
No proporciona un lenguaje de definición de conceptos.
Las restricciones no son mapas conceptuales. El usuario no necesita anotar específicamente sus tipos para cumplir con ciertas restricciones. Son características de lenguaje simple en tiempo de compilación comprobadas estáticamente.
Las implementaciones de plantillas no están limitadas por las limitaciones de sus argumentos de plantilla. Es decir, si su plantilla de función hace algo con un objeto de tipo restringido que no debería hacer, el compilador no tiene forma de diagnosticarlo. Una propuesta de conceptos con todas las funciones podría hacer esto.
La propuesta de restricciones se ha diseñado específicamente para que se pueda introducir una propuesta de conceptos completa sobre ella. Con un poco de suerte, esa transición debería ser bastante suave. El grupo de conceptos busca introducir restricciones para C ++ 14 (o en un informe técnico poco después), mientras que los conceptos completos podrían comenzar a surgir en algún momento alrededor de C ++ 17.