Dado:
#include <concepts>
#include <iostream>
template<class T>
struct wrapper;
template<std::signed_integral T>
struct wrapper<T>
{
wrapper() = default;
void print()
{
std::cout << "signed_integral" << std::endl;
}
};
template<std::integral T>
struct wrapper<T>
{
wrapper() = default;
void print()
{
std::cout << "integral" << std::endl;
}
};
int main()
{
wrapper<int> w;
w.print(); // Output : signed_integral
return 0;
}
Del código anterior, intcalifica a ambos std::integraly al std::signed_integralconcepto.
Sorprendentemente, esto compila e imprime "igned_integral "en los compiladores GCC y MSVC. Esperaba que fallara con un error en la línea de "especialización de plantilla ya definida".
Bien, eso es legal, bastante justo, pero ¿por qué fue std::signed_integralelegido en lugar de std::integral? ¿Hay alguna regla definida en el estándar con qué especialización de plantilla se elige cuando varios conceptos califican para el argumento de plantilla?
c++
language-lawyer
c++20
c++-concepts
Lewis Liman
fuente
fuente

Respuestas:
Esto se debe a que los conceptos pueden ser más especializados que otros, como el orden de las plantillas. Esto se llama ordenación parcial de restricciones.
En el caso de los conceptos, se subsumen entre sí cuando incluyen restricciones equivalentes. Por ejemplo, aquí se explica cómo
std::integraly cómostd::signed_integralse implementan:Normalizando las restricciones, el compilador reduce la expresión de restricción a esto:
En este ejemplo,
signed_integralimplicaintegralcompletamente. Entonces, en cierto sentido, una integral con signo está "más restringida" que una integral.El estándar lo escribe así:
De [temp.func.order] / 2 (el énfasis es mío):
Eso significa que si hay múltiples sustituciones posibles para una plantilla y ambas se eligen del orden parcial, seleccionará la plantilla más restringida.
Desde [temp.constr.order] / 1 :
Esto describe el algoritmo de subsunción que el compilador usa para ordenar las restricciones y, por lo tanto, los conceptos.
fuente
C ++ 20 tiene un mecanismo para decidir cuándo una entidad restringida particular está "más restringida" que otra. Esto no es una cosa simple.
Esto comienza con el concepto de dividir una restricción en sus componentes atómicos, un proceso llamado normalización de restricción . Es grande y demasiado complejo entrar aquí, pero la idea básica es que cada expresión en una restricción se desglosa en sus piezas conceptuales atómicas, recursivamente, hasta llegar a una sub-expresión componente que no es un concepto.
Por lo tanto, veamos cómo se definen los conceptos
integraly :signed_integralLa descomposición de
integrales justais_integral_v. La descomposición designed_integralesis_integral_v && is_signed_v.Ahora, llegamos al concepto de subsunción de restricción . Es un poco complicado, pero la idea básica es que se dice que una restricción C1 "subsume" una restricción C2 si la descomposición de C1 contiene todas las subexpresiones en C2. Podemos ver que
integralno subsumesigned_integral, perosigned_integrallo hace subsumeintegral, ya que contiene todo lo queintegralhace.A continuación, llegamos a ordenar entidades restringidas:
Como
signed_integralsubsumesintegral, el<signed_integral> wrapperes "al menos tan restringido" como el<integral> wrapper. Sin embargo, lo contrario no es cierto, debido a que la subsunción no es reversible.Por lo tanto, de acuerdo con la regla para entidades "más restringidas":
Como
<integral> wrapperno está al menos tan restringido como<signed_integral> wrapper, este último se considera más restringido que el primero.Y por lo tanto, cuando los dos podrían postularse, gana la declaración más restringida.
Tenga en cuenta que las reglas de subsunción de restricción se detienen cuando se encuentra una expresión que no es a
concept. Entonces, si hiciste esto:En este caso,
my_signed_integralno subsumiríastd::integral. Aunquemy_is_integral_vse define de manera idénticastd::is_integral_v, porque no es un concepto, las reglas de subsunción de C ++ no pueden analizarlo para determinar si son lo mismo.Por lo tanto, las reglas de subsunción lo alientan a construir conceptos a partir de operaciones sobre conceptos atómicos.
fuente
Con Parcial_ordering_of_constraints
y
Y el concepto
std::signed_integralsubsume elstd::integral<T>concepto:Entonces su código está bien, ya que
std::signed_integrales más "especializado".fuente