¿Qué es el procedimiento de pedido parcial en la deducción por plantilla?

Resuelto yuan asked hace 11 años • 1 respuestas

Al leer el estándar C++ 11, no puedo entender completamente el significado de la siguiente declaración. Los ejemplos son muy bienvenidos.

Se utilizan dos conjuntos de tipos para determinar el orden parcial. Para cada una de las plantillas involucradas existe 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 utiliza 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 orden parcial: una vez usando la plantilla-1 transformada como plantilla de argumento y la plantilla-2 como plantilla de parámetro y nuevamente usando la plantilla-2 transformada como plantilla de argumento y plantilla-1. como plantilla de parámetros
- N3242 14.8.2.4.2

yuan avatar Jun 09 '13 09:06 yuan
Aceptado

Si bien Xeo dio una descripción bastante buena en los comentarios , intentaré dar una explicación paso a paso con un ejemplo práctico.

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

Para cada una de las plantillas involucradas existe 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 parámetro de plantilla de tipo, no tipo o de plantilla (incluidos los paquetes de parámetros de plantilla (14.5.3) del mismo), sintetice una plantilla de tipo, valor o clase única, respectivamente, y sustitúyala por cada aparición de ese parámetro. en el tipo de función de la plantilla [...]

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

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

Ahora bien, dado que Ty Uson parámetros de tipo, el párrafo anterior nos pide que elijamos un argumento de tipo correspondiente para T(lo que sea) y lo sustituyamos en todas partes de la firma de función donde Taparece, y luego hagamos lo mismo para U.

Ahora bien, " sintetizar un tipo único " significa que tienes que elegir un tipo ficticio que no hayas usado en ningún otro lugar, y podríamos llamarlo así P1(y luego elegir un P2for U), pero eso haría que nuestra discusión fuera inútilmente formal.

Simplifiquemos las cosas y elijamos intpor Ty boolpara U; no usamos esos tipos en ningún otro lugar, por lo que para nuestros propósitos, son tan buenos como P1y P2.

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

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

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

Así que sigamos interpretando el párrafo que citaste. La segunda frase 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 "? foo()Hasta ahora solo tenemos una sobrecarga . Correcto, pero para establecer un orden entre las plantillas de funciones, necesitamos al menos dos de ellas, por lo que será mejor que creemos una segunda. Usemos:

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

¿ Dónde Xhay alguna plantilla de clase nuestra?

¿Y ahora qué pasa con esta segunda plantilla de función? Ah, sí, debemos hacer lo mismo que hicimos anteriormente para la primera sobrecarga foo()y transformarlo: así que nuevamente, elijamos algún argumento de tipo Ty reemplácelo Ten todas partes. Elegiré chareste momento (no lo usaremos en ningún otro lugar de este ejemplo, por lo que es tan bueno como algo ficticio P3):

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 #1es más especializado #2o viceversa?

Lo que sabemos de 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 explica la tercera frase:

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

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

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

Empecemos. En primer lugar tendremos que unir:

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

Contra:

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

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

Difícilmente. Así que intentemos hacer coincidir al revés. Debemos hacer coincidir:

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

Contra:

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

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

Entonces descubrimos que el tipo de función transformada de nuestra primera sobrecarga de foo()( #1b) no puede compararse 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 ( #2b) se puede comparar con la plantilla de función original de la primera sobrecarga ( #1).

¿Conclusión? La segunda sobrecarga 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 usted puede hacerlo, y eso debería convencerlo de que no se puede producir una coincidencia en ninguna dirección, ya que la primera sobrecarga es más especializada que la segunda en lo que respecta al primer parámetro. pero el segundo es más especializado que el primero en lo que respecta al segundo parámetro.

¿Conclusión? Ninguna plantilla de función es más especializada que la otra.

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

Tenga en cuenta también que el mismo mecanismo descrito anteriormente se utiliza 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 del Estándar 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 de dos plantillas de funciones, la primera plantilla de funciones es al menos tan especializada como la segunda de acuerdo con las reglas de ordenamiento para plantillas de funciones (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.

Andy Prowl avatar Jun 09 '2013 10:06 Andy Prowl