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_trait
toma 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
temped
plantilla 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 T
y U
en 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?
c++
templates
sfinae
template-specialization
Anuncio N
fuente
fuente
Respuestas:
La respuesta ahora eliminada de @ super acertó básicamente.
std::enable_if_t<...>
no estávoid
en 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.
fuente
std::enable_if_t<std::is_same<DummyT, DummyT>::value>
dondeDummyT
hay un tipo teórico "sintetizado" pero específico ( [temp.func.order] / 3 ).std_enable_if_t<>
proporciona una especialización parcial preferida en la declaracióntrait_t<int, int>::value
, por lo que parece que se consideravoid
(de lo contrario, parece que esta expresión debería ser falsa seleccionando la plantilla base). Por otro lado, la declaracióntrait_t<temped<int>, temped<int>>::value
conduce a una ambigüedad, aunque la especialización parcialmy_trait<temped<T>, temped<T>, void>
que esperábamos sería más especializada, establece explícitamente elvoid
tipo.eso es porque
resuelve anular y luego tienes
definido ambiguamente como verdadero o falso. Si cambia el tipo enable_if resuelve a, digamos bool, todo se compila bien
fuente
std::enable_if_t<*, bool>
, entonces es como una derrota del propósito (porque ni siquiera está habilitado para la especialización).