c++ templates c++11 partial-ordering template-deduction

c++ - ¿Cuál es el procedimiento de pedido parcial en la deducción de plantilla?



templates c++11 (1)

Leí sobre el estándar c ++ 11 pero no puedo entender bien el significado de lo siguiente. Por ejemplo, son muy preferidos.

Se usan dos conjuntos de tipos para determinar el orden parcial. Para cada una de las plantillas involucradas, está el tipo de función original y el tipo de función transformada. [Nota: la creación del tipo transformado se describe en 14.5.6.2. - nota final] El proceso de deducción usa el tipo transformado como plantilla de argumento y el tipo original de la otra plantilla como plantilla de parámetro. Este proceso se realiza dos veces para cada tipo involucrado en la comparación de ordenamiento parcial: una vez usando la plantilla-1 transformada como la plantilla de argumento y la plantilla-2 como la plantilla de parámetro y nuevamente usando la plantilla-2 transformada como la plantilla de argumento y plantilla-1 como la plantilla de parámetro
- N3242 14.8.2.4.2


Si bien Xeo dio una descripción bastante buena en los comentarios , trataré de dar una explicación paso a paso con un ejemplo de trabajo.

En primer lugar, la primera frase del párrafo que citó dice:

Para cada una de las plantillas involucradas, está el tipo de función original y el tipo de función transformada . [...]

Espera, ¿qué es este " tipo de función transformada "? El párrafo 14.5.6.2/3 explica que:

Para producir la plantilla transformada, para cada tipo, no tipo o parámetro de plantilla de plantilla (incluidos los paquetes de parámetros de plantilla (14.5.3)) sintetizan un tipo único, valor o plantilla de clase, respectivamente, y lo sustituyen por cada aparición de ese parámetro en el tipo de función de la plantilla [...]

Esta descripción formal puede sonar oscura, pero en realidad es muy simple en la práctica. Tomemos esta plantilla de función como ejemplo:

template<typename T, typename U> void foo(T, U) // #1

Ahora que T y U son parámetros de tipo, el párrafo anterior nos pide que elijamos un argumento de tipo correspondiente para T (lo que sea) y lo sustituimos en todas partes en la firma de función donde aparece T , y luego hacemos lo mismo para U

Ahora, " sintetizar un tipo único " significa que debe elegir un tipo ficticio que no haya utilizado en otro lugar, y podríamos llamar a ese P1 (y luego elegir un P2 para U ), pero eso haría que nuestra discusión fuera inútil.

Simplifiquemos las cosas y escojamos int para T y bool para U - no estamos usando esos tipos en ningún otro lado, entonces para nuestros propósitos, son tan buenos como P1 y P2 .

Entonces, después de la transformación, tenemos:

void foo(int, bool) // #1b

Este es el tipo de función transformada para nuestra plantilla de función foo() original.

Así que continuemos interpretando el párrafo que citó. La segunda oración dice:

El proceso de deducción utiliza el tipo transformado como plantilla de argumento y el tipo original de la otra plantilla como plantilla de parámetro. [...]

Espera, ¿qué " otra plantilla "? Solo tenemos una sobrecarga de foo() hasta el momento. Correcto, pero para establecer un orden entre plantillas de funciones, necesitamos al menos dos de ellas, así que será mejor que creemos una segunda. Usemos:

template<typename T> void foo(T const*, X<T>) // #2

Donde X es una plantilla de nuestra clase.

Ahora, ¿qué pasa con esta segunda plantilla de función? Ah, sí, tenemos que hacer lo mismo que hicimos anteriormente para la primera sobrecarga de foo() y transformarla: así que de nuevo, escojamos algún argumento de tipo para T y reemplazamos T todas partes. Voy a elegir char esta vez (no lo estamos usando en ningún otro lado en este ejemplo, así que es tan bueno como un P3 ficticio):

void foo(char const*, X<char>) #2b

Genial, ahora tiene dos plantillas de funciones y los tipos de funciones transformadas correspondientes. Entonces, ¿cómo determinar si el #1 es más especializado que el #2 o viceversa?

Lo que sabemos por la oración anterior es que las plantillas originales y sus tipos de funciones transformadas deben coincidir de alguna manera. ¿Pero cómo? Eso es lo que la tercera oración explica:

Este proceso se realiza dos veces para cada tipo involucrado en la comparación de ordenamiento parcial: una vez usando la plantilla-1 transformada como la plantilla de argumento y la plantilla-2 como la plantilla de parámetro y nuevamente usando la plantilla-2 transformada como la plantilla de argumento y plantilla-1 como la plantilla de parámetro

Entonces, básicamente, el tipo de función transformada de la primera plantilla (n #1b ) se comparará con el tipo de función de la segunda plantilla original (n. #2 ). Y, por supuesto, al revés, el tipo de función transformada de la segunda segunda plantilla (n #2b ) debe compararse con el tipo de función de la primera plantilla original (n. #1 ).

Si la coincidencia tendrá éxito en una dirección pero no en la otra, sabremos que una de las plantillas es más especializada que la otra. De lo contrario, ninguno es más especializado.

Empecemos. Antes que nada, tendremos que coincidir:

void foo(int, bool) // #1b

En contra:

template<typename T> void foo(T const*, X<T>) // #2

¿Hay alguna manera de que podamos realizar la deducción del tipo en T para que T const* convierta exactamente en int y X<T> convierta exactamente en bool ? (en realidad, no es necesaria una coincidencia exacta , pero hay muy pocas excepciones a esta regla y no son relevantes para ilustrar el mecanismo de ordenamiento parcial, por lo que las ignoraremos).

Apenas. Intentemos coincidir al revés. Deberíamos unir:

void foo(char const*, X<char>) // #2b

En contra:

template<typename T, typename U> void foo(T, U) // #1

¿Podemos deducir T y U aquí para producir una coincidencia exacta para char const* y X<char> , respectivamente? ¡Por supuesto! Es trivial. Simplemente seleccionamos T = char const* y U = X<char> .

Así que descubrimos que el tipo de función transformada de nuestra primera sobrecarga de foo() ( #1b ) no se puede comparar con la plantilla de función original de nuestra segunda sobrecarga de foo() ( #2 ); por otro lado, el tipo de función transformada de la segunda sobrecarga (n #2b ) se puede comparar con la plantilla de función original de la primera sobrecarga (n. #1 ).

¿Conclusión? La segunda sobrecarga de foo() es más especializada que la primera.

Para elegir un contraejemplo, considere estas dos plantillas de funciones:

template<typename T, typename U> void bar(X<T>, U) template<typename T, typename U> void bar(U, T const*)

¿Qué sobrecarga es más especializada que la otra? No repetiré todo el procedimiento nuevamente, pero puedes hacerlo, y eso debería convencerte de que no se puede producir una coincidencia en ninguna dirección, ya que la primera sobrecarga es más especializada que la segunda por lo que respecta al primer parámetro, pero el segundo es más especializado que el primero para lo que concierne al segundo parámetro.

¿Conclusión? Ninguna de las plantillas de funciones es más especializada que la otra.

Ahora en esta explicación he ignorado muchos detalles, excepciones a las reglas y pasajes crípticos en el Estándar, pero el mecanismo delineado en el párrafo que citó es de hecho este.

Observe también, que el mismo mecanismo descrito anteriormente se usa para establecer un orden " más especializado que " entre especializaciones parciales de una plantilla de clase creando primero una plantilla de función ficticia asociada para cada especialización, y luego ordenando esas plantillas de función a través del Algoritmo descrito en esta respuesta.

Esto se especifica en el párrafo 14.5.5.2/1 de la Norma C ++ 11:

Para dos especializaciones parciales de plantilla de clase, la primera es al menos tan especializada como la segunda si, dada la siguiente reescritura en dos plantillas de función, la primera plantilla de función es al menos tan especializada como la segunda de acuerdo con las reglas de ordenamiento para plantillas de función (14.5 .6.2):

- la primera plantilla de función tiene los mismos parámetros de plantilla que la primera especialización parcial y tiene un único parámetro de función cuyo tipo es una especialización de plantilla de clase con los argumentos de plantilla de la primera especialización parcial, y

- la segunda plantilla de función tiene los mismos parámetros de plantilla que la segunda especialización parcial y tiene un único parámetro de función cuyo tipo es una especialización de plantilla de clase con los argumentos de plantilla de la segunda especialización parcial.

Espero que esto haya ayudado.