una total resueltos relación relacion qué parcial orden matematicas estricto equivalencia ejercicios ejemplos discretas cadena antisimetrica c++ templates language-lawyer partial-ordering

c++ - total - Especialización de la plantilla de clase, ordenamiento parcial y síntesis de funciones



relacion de orden parcial matematicas discretas (1)

Clang está siendo compatible con GCC (y compatible con el código existente que depende de estos dos comportamientos).

Considere [temp.deduct.type] p1 :

[...] se intenta encontrar valores de argumento de plantilla (un tipo para un parámetro de tipo, un valor para un parámetro que no sea de tipo, o una plantilla para un parámetro de plantilla) que harán P, después de la sustitución de los valores deducidos (llámalo el deducido A), compatible con A.

El quid de la cuestión es qué significa "compatible" aquí.

Cuando se ordenan parcialmente plantillas de funciones, Clang simplemente deduce en ambas direcciones; si la deducción tiene éxito en una dirección pero no en la otra, se supone que significa que el resultado será "compatible" y lo utiliza como resultado de ordenamiento.

Sin embargo, cuando ordena parcialmente las especializaciones parciales de la plantilla de clase, Clang interpreta que "compatible" significa "lo mismo". Por lo tanto, solo considera que una especialización parcial debe ser más especializada que otra si al sustituir los argumentos deducidos de uno de ellos en el otro se reproduciría la especialización parcial original.

Cambiar cualquiera de estos dos para que coincida con el otro rompe cantidades sustanciales de código real. :(

Las reglas para elegir qué especialidad de plantilla de clase es preferible implican reescribir las especializaciones en plantillas de funciones y determinar qué plantilla de función es más especializada a través de las reglas de ordenamiento para plantillas de funciones [temp.class.order]. Considera este ejemplo, entonces:

#include <iostream> template <class T> struct voider { using type = void; }; template <class T> using void_t = typename voider<T>::type; template <class T, class U> struct A { }; template <class T> int foo(A<T, void_t<T>> ) { return 1; } template <class T> int foo(A<T*, void> ) { return 2; } int main() { std::cout << foo(A<int*, void>{}); }

Tanto gcc como clang imprimen 2 aquí. Esto tiene sentido con algunos ejemplos anteriores: la deducción frente a un contexto no deducido ( void contra void_t<T> ) simplemente se ignora, por lo que deducir <T, void_t<T>> contra <X*, void> tiene éxito pero deducir <T*, void> contra <Y, void_t<Y>> falla en ambos argumentos. Multa.

Ahora considere esta generalización:

#include <iostream> template <class T> struct voider { using type = void; }; template <class T> using void_t = typename voider<T>::type; template <int I> struct int_ { static constexpr int value = I; }; template <class T, class U> struct A : int_<0> { }; template <class T> struct A<T, void_t<T>> : int_<1> { }; template <class T> struct A<T*, void> : int_<2> { }; int main() { std::cout << A<int*, void>::value << ''/n''; }

Tanto clang como gcc informan esta especialización como ambigua, entre 1 y 2 . ¿Pero por qué? Las plantillas de funciones sintetizadas no son ambiguas. ¿Cuál es la diferencia entre estos dos casos?