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, int
califica a ambos std::integral
y al std::signed_integral
concepto.
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_integral
elegido 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::integral
y cómostd::signed_integral
se implementan:Normalizando las restricciones, el compilador reduce la expresión de restricción a esto:
En este ejemplo,
signed_integral
implicaintegral
completamente. 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
integral
y :signed_integral
La descomposición de
integral
es justais_integral_v
. La descomposición designed_integral
esis_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
integral
no subsumesigned_integral
, perosigned_integral
lo hace subsumeintegral
, ya que contiene todo lo queintegral
hace.A continuación, llegamos a ordenar entidades restringidas:
Como
signed_integral
subsumesintegral
, el<signed_integral> wrapper
es "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> wrapper
no 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_integral
no subsumiríastd::integral
. Aunquemy_is_integral_v
se 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_integral
subsume elstd::integral<T>
concepto:Entonces su código está bien, ya que
std::signed_integral
es más "especializado".fuente