Gegeben :
#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;
}
int
Qualifiziert sich aus dem obigen Code sowohl für das Konzept std::integral
als auch für das std::signed_integral
Konzept.
Überraschenderweise kompiliert und druckt dies "signiertes_Integral" sowohl auf GCC- als auch auf MSVC-Compilern. Ich hatte erwartet, dass es mit einem Fehler im Sinne von "Template-Spezialisierung bereits definiert" fehlschlagen würde.
Okay, das ist legal, fair genug, aber warum wurde statt std::signed_integral
gewählt std::integral
? Gibt es im Standard Regeln, mit denen die Vorlagenspezialisierung ausgewählt wird, wenn sich mehrere Konzepte für das Vorlagenargument qualifizieren?
c++
language-lawyer
c++20
c++-concepts
Lewis Liman
quelle
quelle
Antworten:
Dies liegt daran, dass Konzepte spezialisierter sein können als andere, ähnlich wie die Reihenfolge der Vorlagen selbst. Dies wird als Teilreihenfolge von Einschränkungen bezeichnet
Bei Konzepten subsumieren sie sich gegenseitig, wenn sie äquivalente Einschränkungen enthalten. Zum Beispiel, wie
std::integral
undstd::signed_integral
implementiert werden:Wenn der Compiler die Einschränkungen normalisiert, reduziert er den Contraint-Ausdruck auf Folgendes:
In diesem Beispiel
signed_integral
impliziertintegral
vollständig. In gewissem Sinne ist ein vorzeichenbehaftetes Integral "eingeschränkter" als ein Integral.Der Standard schreibt es so:
Aus [temp.func.order] / 2 (Schwerpunkt Mine):
Das heißt, wenn eine Vorlage mehrfach ersetzt werden kann und beide aus der Teilreihenfolge ausgewählt werden, wird die am stärksten eingeschränkte Vorlage ausgewählt.
Aus [temp.constr.order] / 1 :
Dies beschreibt den Subsumtionsalgorithmus, den der Compiler verwendet, um Einschränkungen und damit Konzepte zu ordnen.
quelle
C ++ 20 verfügt über einen Mechanismus zum Entscheiden, wann eine bestimmte eingeschränkte Entität "stärker eingeschränkt" ist als eine andere. Das ist keine einfache Sache.
Dies beginnt mit dem Konzept, eine Einschränkung in ihre atomaren Komponenten zu zerlegen, ein Prozess, der als Einschränkungsnormalisierung bezeichnet wird . Es ist groß und zu komplex, um hier darauf einzugehen, aber die Grundidee ist, dass jeder Ausdruck in einer Einschränkung rekursiv in seine atomaren konzeptuellen Teile zerlegt wird, bis Sie einen Komponenten-Unterausdruck erreichen, der kein Konzept ist.
Schauen wir uns vor diesem Hintergrund an, wie die
integral
undsigned_integral
Konzepte definiert sind :Die Zersetzung von
integral
ist gerechtis_integral_v
. Die Zersetzung vonsigned_integral
istis_integral_v && is_signed_v
.Nun kommen wir zum Konzept der Constraint-Subsumtion . Es ist etwas kompliziert, aber die Grundidee ist, dass eine Einschränkung C1 eine Einschränkung C2 "subsumiert", wenn die Zerlegung von C1 jeden Unterausdruck in C2 enthält. Wir können sehen , dass
integral
nicht nicht subsumierensigned_integral
, sondernsigned_integral
tut subsumeintegral
, da es alles enthältintegral
tut.Als nächstes kommen wir zur Bestellung von eingeschränkten Entitäten:
Weil
signed_integral
subsumiertintegral
,<signed_integral> wrapper
ist das "mindestens so eingeschränkt" wie das<integral> wrapper
. Das Gegenteil ist jedoch nicht der Fall, da die Subsumtion nicht umkehrbar ist.In Übereinstimmung mit der Regel für "eingeschränktere" Entitäten:
Da das
<integral> wrapper
nicht mindestens so eingeschränkt ist wie<signed_integral> wrapper
das letztere, wird das letztere als stärker eingeschränkt angesehen als das erstere.Und daher gewinnt die eingeschränktere Deklaration, wenn beide zutreffen könnten.
Beachten Sie, dass die Regeln für die Subsumtion von Einschränkungen aufhören, wenn ein Ausdruck gefunden wird, der kein a ist
concept
. Wenn Sie dies getan haben:In diesem Fall
my_signed_integral
würde nicht subsumierenstd::integral
. Obwohl C ++my_is_integral_v
identisch definiert iststd::is_integral_v
, weil es kein Konzept ist, können die Subsumtionsregeln von C ++ nicht durchsehen, um festzustellen, ob sie identisch sind.Die Subsumtionsregeln ermutigen Sie daher, Konzepte aus Operationen auf atomaren Konzepten zu erstellen.
quelle
Mit Partial_ordering_of_constraints
und
Und Konzept
std::signed_integral
fasststd::integral<T>
Konzept zusammen:Ihr Code ist also in Ordnung, ebenso wie
std::signed_integral
"spezialisierter".quelle