¿Cuáles son las diferencias entre conceptos y restricciones de plantilla?

96

Quiero saber cuáles son las diferencias semánticas entre la propuesta de conceptos completos de C ++ y las restricciones de la plantilla (por ejemplo, las restricciones que aparecieron en Dlang o la nueva propuesta de conceptos básicos para C ++ 1y ).

¿Qué son capaces de hacer los conceptos completos que las restricciones de plantilla no pueden hacer?

Rayniery
fuente
2
La propuesta de restricciones entra en esto.
chris
Estaba recordando 4.8 Conceptos de diseño , pero en realidad no se enumeran muchas cosas sobre cómo los conceptos aumentan las restricciones. En todo caso, buscar conceptos en Google ahora podría revelar algunas diferencias fácilmente detectables después de obtener una mejor comprensión de las limitaciones de la propuesta.
chris
En mi humilde opinión, los conceptos mejoran la legibilidad y proporcionan habilidades programáticas más claras como lo solicitaron hace mucho tiempo personas como Alexander Stepanov en 'Elementos de programación'. La propuesta lite es solo un paso hacia esto para aliviar la carga de las extrañas restricciones de tipo enable_if que se requieren en este momento. Cuanto antes, mejor para la programación genérica.
dirvine

Respuestas:

135

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:

  1. Restricción: un predicado sobre propiedades evaluables estáticamente de un tipo. Requisitos puramente sintácticos. No es una abstracción de dominio.
  2. Axiomas: requisitos semánticos de tipos que se suponen verdaderos. No verificado estáticamente.
  3. 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 Contdebe ser un Sortabletipo. 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 Sortablea esta función, obtendrá un error agradable que le indicará inmediatamente que el tipo deducido Tno es un Sortabletipo. Si hubiera hecho esto en C ++ 11, habría tenido un error horrible desde dentro de la sortfunció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:

  1. ¿Este tipo tiene tal o cual operador sobrecargado?
  2. ¿Se pueden utilizar estos tipos como operandos para este operador?
  3. ¿Este tipo tiene tal o cual rasgo?
  4. ¿Es esta expresión constante igual a eso? (para argumentos de plantilla que no son de tipo)
  5. ¿Este tipo tiene una función llamada yada-yada que devuelve ese tipo?
  6. ¿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 Ty 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 Greateraxioma. Está muy bien decir que dos objetos de tipo Tse pueden comparar con operadores >y <, pero ¿qué significan ? El Greateraxioma dice que sif xes mayor entonces y, entonces yes 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:

  1. ¿Estos dos operadores tienen esta relación entre sí?
  2. ¿Este operador para tal o cual tipo significa esto?
  3. ¿Tiene esta operación en ese tipo esta complejidad?
  4. ¿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 xy yde tipo T: si x == y, un nuevo objeto del mismo tipo creado por construcción de copia T{x} == yy 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 Orderedconcepto:

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 Tsea Ordered, también debe cumplir con los requisitos del Regularconcepto. El Regularconcepto 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 Orderedrequiere que Tcumplan una restricción y cuatro axiomas:

  • Restricción: un Orderedtipo debe tener una extensión operator<. Esto se comprueba estáticamente, por lo que debe existir.
  • Axiomas: Para xy yde tipo T:
    • x < y da un orden total estricto.
    • Cuando xes mayor que y, yes menor que xy viceversa.
    • Cuando xes menor o igual a y, yno es menor que xy viceversa.
    • Cuando xes mayor o igual que y, yno es mayor que xy 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.

  • Orderedtipos de soporte operator<, y tienen un orden total estricto y otra semántica de orden.

  • Copyablelos tipos son copiables, destruibles, y si xes igual a yy xse copia, la copia también se comparará con y.

  • Iteratortipos tipos deben haber asociadas value_type, reference, difference_type, y iterator_categoryque 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_ify sus amigos de la metaprogramación.

Sin embargo, hay una serie de cosas que la propuesta de restricciones no hace:

  1. No proporciona un lenguaje de definición de conceptos.

  2. 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.

  3. 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.

10 revoluciones
fuente
5
Cabe señalar que concept-lite no contrasta las limitaciones con la implementación de la plantilla en sí. Por lo tanto, podría afirmar que se puede usar cualquier DefaultConstructable, pero el compilador no le impedirá usar un constructor de copia por accidente. Una propuesta de conceptos más completa lo haría.
Nicol Bolas
24
¡¿Eso es un primer borrador ?!
Nicol Bolas
2
@sftrabbit, muy buena respuesta. Pero tengo una pregunta: ¿cómo comprobará un compilador que un tipo cumple los requisitos semánticos de un concepto?
Rayniery
1
¿Se verificarán (con mucha incertidumbre) los 'axiomas' durante el tiempo de ejecución, o solo servirán como una especie de etiquetas de promesa?
Rojo XIII
4
@ScarletAmaranth: Porque se reduce a demostrar automáticamente un teorema en un tiempo limitado finito. Hay dos obstáculos para esto: 1. Probar cualquier teorema en tiempo limitado finito, lo cual es extremadamente difícil en teoría e imposible con la tecnología actual. 2. No se puede probar un axioma, a menos que la prueba se base en otros axiomas. (En cuyo caso matemáticamente se convierte en "no es un axioma") Estos axiomas están destinados a decirle al compilador "Por supuesto, puede revertir la comparación si lo considera útil ...", o "Sí, un EmptySet usado en una intersección siempre da el mismo resultado ".
Laurent LA RIZZA
4

Mis 2 centavos:

  1. La propuesta de conceptos básicos no está destinada a realizar una "verificación de tipos" de la implementación de la plantilla . Es decir, Concepts-lite asegurará (teóricamente) la compatibilidad de la interfaz en el sitio de creación de instancias de la plantilla. Citando del artículo: "Conceptos Lite es una extensión de C ++ que permite el uso de predicados para restringir los argumentos de la plantilla". Y eso es. No dice que el cuerpo de la plantilla se comparará (de forma aislada) con los predicados. Eso probablemente significa que no existe una noción de arquetipos de primera clase cuando se habla de conceptos-lite. Los arquetipos, si mal no recuerdo, en la propuesta de conceptos pesados ​​son tipos que ofrecen nada menos y nada más para satisfacer la implementación de la plantilla.

  2. concept-lite usa funciones constexpr glorificadas con un pequeño truco de sintaxis compatible con el compilador. Sin cambios en las reglas de búsqueda.

  3. Los programadores no están obligados a escribir mapas de conceptos.

  4. Finalmente, citando de nuevo "La propuesta de restricciones no aborda directamente la especificación o el uso de la semántica; está dirigida solo a verificar la sintaxis". Eso significaría que los axiomas no están dentro del alcance (hasta ahora).

Sumant
fuente