¿Por qué una especialización de plantilla de clase parcial en una clase de plantilla coincidente es ambigua con otra especialización parcial sin la coincidencia de plantilla?

8

La pregunta puede ser demasiado difícil de describir en una oración del título, pero aquí hay un ejemplo mínimo:

#include <iostream>
#include <type_traits>

template <class T, class U, class Enabler>
struct my_trait : std::false_type
{};

template <class T, class U>
struct my_trait<T, U, 
                std::enable_if_t<std::is_same<T, U>::value>> : std::true_type
{};

template <class T>
class temped
{};

template <class T>
struct my_trait<temped<T>, temped<T>, void> : std::false_type
{};

template <class T, class U>
using trait_t = my_trait<T, U, void>;

int main()
{
    std::cout << std::boolalpha;
    std::cout << trait_t<int, float>::value << std::endl;   // false
    std::cout << trait_t<int, int>::value << std::endl;     // true

    // Compilation error: Ambiguous
    //std::cout << trait_t<temped<int>, temped<int>>::value << std::endl;
    return 0;    
}

( también disponible en godbolt )

Básicamente, tenemos una clase de plantilla base que my_traittoma dos tipos (y un tipo ficticio para fines de especialización), con dos especializaciones parciales:

  • Cuando los dos tipos son iguales
  • Cuando los dos tipos son instanciación de la tempedplantilla de clase para el mismo tipo

Ingenuamente, hubiéramos esperado que la segunda especialización parcial no fuera ambigua con la primera, ya que se siente "más especializada", poniendo más restricción en los tipos deducidos para Ty Uen la plantilla base. Sin embargo, los principales compiladores parecen estar de acuerdo en que estábamos equivocados con nuestras expectativas: ¿por qué no se considera más especializado?

Anuncio N
fuente
Pregunta relacionada: stackoverflow.com/q/17412686/9171697
Rin Kaenbyou

Respuestas:

1

La respuesta ahora eliminada de @ super acertó básicamente. std::enable_if_t<...>no está voiden orden parcial; como tipo dependiente, en principio puede ser algo completamente arbitrario. Efectivamente se considera un tipo completamente único para fines de pedidos parciales.

Como resultado de este desajuste, la deducción durante el pedido parcial falla en ambas direcciones, y las especializaciones son ambiguas.

TC
fuente
1
Pero no es realmente un tipo dependiente cuando se hace un pedido parcial, ¿verdad? Es std::enable_if_t<std::is_same<DummyT, DummyT>::value>donde DummyThay un tipo teórico "sintetizado" pero específico ( [temp.func.order] / 3 ).
Aschepler
¿Quizás un "tipo único sintetizado" también implica que podría haber especializaciones adicionales usando ese tipo, incluso si ninguna especialización actualmente visible podría hacer la diferencia? Pero, ¿dice el Estándar eso en alguna parte?
Aschepler
Puede que me esté perdiendo su punto: std_enable_if_t<>proporciona una especialización parcial preferida en la declaración trait_t<int, int>::value, por lo que parece que se considera void(de lo contrario, parece que esta expresión debería ser falsa seleccionando la plantilla base). Por otro lado, la declaración trait_t<temped<int>, temped<int>>::valueconduce a una ambigüedad, aunque la especialización parcial my_trait<temped<T>, temped<T>, void>que esperábamos sería más especializada, establece explícitamente el voidtipo.
Anuncio N
Hay un problema abierto relacionado con este: wg21.link/CWG2160 Sin embargo, en ese caso, la deducción aparentemente tiene éxito en una dirección (?), Por lo que no puedo conciliar ese ejemplo con este.
Brian
1
Y en cualquier caso, la respuesta "real" a la pregunta de OP debería ser: el estándar no está claro en esto, así que no escriba código como este hasta que el estándar lo aclare.
Brian
0

eso es porque

std::enable_if_t<std::is_same_v<temped<int>, temped<int>> 

resuelve anular y luego tienes

my_trait<temped<int>, temped<int>, void>::value 

definido ambiguamente como verdadero o falso. Si cambia el tipo enable_if resuelve a, digamos bool, todo se compila bien

Yamahari
fuente
1
El OP pregunta por qué es ambiguo, en el contexto del ordenamiento parcial de especialización de plantilla. Si cambias a std::enable_if_t<*, bool>, entonces es como una derrota del propósito (porque ni siquiera está habilitado para la especialización).
Rin Kaenbyou el