Einschränkungen und Konzepte
Diese Seite beschreibt ein experimentelles Core-Language-Feature. Für benannte Typanforderungen, die in der Spezifikation der Standardbibliothek verwendet werden, siehe benannte Anforderungen
Klassentemplates, Funktionstemplates und Nicht-Template-Funktionen (typischerweise Member von Klassentemplates) können mit einer Einschränkung verbunden sein, die die Anforderungen an Template-Argumente spezifiziert, was verwendet werden kann, um die am besten geeigneten Funktionsüberladungen und Template-Spezialisierungen auszuwählen.
Einschränkungen können auch verwendet werden, um die automatische Typableitung bei Variablendeklarationen und Funktionsrückgabetypen auf nur die Typen zu beschränken, die spezifizierte Anforderungen erfüllen.
Benannte Anforderungssätze werden als Konzepte bezeichnet. Jedes Konzept ist ein zur Kompilierzeit ausgewertetes Prädikat und wird Teil der Schnittstelle eines Templates, in dem es als Einschränkung verwendet wird.
#include <string> #include <locale> using namespace std::literals; // Declaration of the concept "EqualityComparable", which is satisfied by // any type T such that for values a and b of type T, // the expression a==b compiles and its result is convertible to bool template<typename T> concept bool EqualityComparable = requires(T a, T b) { { a == b } -> bool; }; void f(EqualityComparable&&); // declaration of a constrained function template // template<typename T> // void f(T&&) requires EqualityComparable<T>; // long form of the same int main() { f("abc"s); // OK, std::string is EqualityComparable f(std::use_facet<std::ctype<char>>(std::locale{})); // Error: not EqualityComparable }
Verletzungen von Einschränkungen werden zur Kompilierzeit, früh im Template-Instanziierungsprozess, erkannt, was zu leicht verständlichen Fehlermeldungen führt.
std::list<int> l = {3,-1,10}; std::sort(l.begin(), l.end()); //Typical compiler diagnostic without concepts: // invalid operands to binary expression ('std::_List_iterator<int>' and // 'std::_List_iterator<int>') // std::__lg(__last - __first) * 2); // ~~~~~~ ^ ~~~~~~~ // ... 50 lines of output ... // //Typical compiler diagnostic with concepts: // error: cannot call std::sort with std::_List_iterator<int> // note: concept RandomAccessIterator<std::_List_iterator<int>> was not satisfied
Die Absicht von Konzepten ist es, semantische Kategorien (Zahl, Bereich, Reguläre Funktion) anstelle von syntaktischen Beschränkungen (HatPlus, Array) zu modellieren. Gemäß der ISO C++ Core Guideline T.20 ist "die Fähigkeit, eine aussagekräftige Semantik anzugeben, ein definierendes Merkmal eines echten Konzepts im Gegensatz zu einer syntaktischen Einschränkung".
Wenn Feature-Testing unterstützt wird, werden die hier beschriebenen Features durch die Makrokonstante __cpp_concepts mit einem Wert von gleich oder größer 201507 angezeigt.
Inhalt |
[edit] Platzhalter
Der uneingeschränkte Platzhalter auto und eingeschränkte Platzhalter, die die Form concept-name < template-argument-list(optional)> haben, sind Platzhalter für den Typ, der abgeleitet werden soll.
Platzhalter können in Variablendeklarationen (in diesem Fall werden sie aus dem Initialisierer abgeleitet) oder in Funktionsrückgabetypen (in diesem Fall werden sie aus Rückgabeanweisungen abgeleitet) vorkommen.
std::pair<auto, auto> p2 = std::make_pair(0, 'a'); // first auto is int, // second auto is char Sortable x = f(y); // the type of x is deduced from the return type of f, // only compiles if the type satisfies the constraint Sortable auto f(Container) -> Sortable; // return type is deduced from the return statement // only compiles if the type satisfies Sortable
Platzhalter können auch in Parametern vorkommen, in diesem Fall wandeln sie Funktionsdeklarationen in Template-Deklarationen um (eingeschränkt, wenn der Platzhalter eingeschränkt ist).
void f(std::pair<auto, EqualityComparable>); // this is a template with two parameters: // unconstrained type parameter and a constrained non-type parameter
Eingeschränkte Platzhalter können überall dort verwendet werden, wo auto verwendet werden kann, z. B. in generischen Lambda-Deklarationen.
auto gl = [](Assignable& a, auto* b) { a = *b; };
Wenn ein eingeschränkter Typspezifizierer einen Nicht-Typ oder ein Template bezeichnet, aber als eingeschränkter Platzhalter verwendet wird, ist das Programm ill-formed.
template<size_t N> concept bool Even = (N%2 == 0); struct S1 { int n; }; int Even::* p2 = &S1::n; // error, invalid use of a non-type concept void f(std::array<auto, Even>); // error, invalid use of a non-type concept template<Even N> void f(std::array<auto, N>); // OK
[edit] Abgekürzte Templates
Wenn ein oder mehrere Platzhalter in einer Funktionsparameterliste vorkommen, ist die Funktionsdeklaration tatsächlich eine Funktionstemplatedeklaration, deren Template-Parameterliste einen erfundenen Parameter für jeden eindeutigen Platzhalter in der Reihenfolge des Erscheinens enthält.
// short form void g1(const EqualityComparable*, Incrementable&); // long form: // template<EqualityComparable T, Incrementable U> void g1(const T*, U&); // longer form: // template<typename T, typename U> // void g1(const T*, U&) requires EqualityComparable<T> && Incrementable<U>; void f2(std::vector<auto*>...); // long form: template<typename... T> void f2(std::vector<T*>...); void f4(auto (auto::*)(auto)); // long form: template<typename T, typename U, typename V> void f4(T (U::*)(V));
Alle von äquivalenten eingeschränkten Typspezifizierern eingeführten Platzhalter haben denselben erfundenen Template-Parameter. Jeder uneingeschränkte Spezifizierer (auto) führt jedoch immer einen anderen Template-Parameter ein.
void f0(Comparable a, Comparable* b); // long form: template<Comparable T> void f0(T a, T* b); void f1(auto a, auto* b); // long form: template<typename T, typename U> f1(T a, U* b);
Sowohl Funktionstemplates als auch Klassentemplates können mit einer Template-Einführung deklariert werden, die die Syntax concept-name { parameter-list(optional)} hat, in welchem Fall das Schlüsselwort template nicht benötigt wird: Jeder Parameter aus der parameter-list der Template-Einführung wird zu einem Template-Parameter, dessen Art (Typ, Nicht-Typ, Template) durch die Art des entsprechenden Parameters im benannten Konzept bestimmt wird.
Neben der Deklaration eines Templates assoziiert die Template-Einführung eine Prädikats-Einschränkung (siehe unten), die das durch die Einführung benannte Konzept benennt (bei Variablenkonzepten) oder aufruft (bei Funktionskonzepten).
EqualityComparable{T} class Foo; // long form: template<EqualityComparable T> class Foo; // longer form: template<typename T> requires EqualityComparable<T> class Foo; template<typename T, int N, typename... Xs> concept bool Example = ...; Example{A, B, ...C} struct S1; // long form template<class A, int B, class... C> requires Example<A,B,C...> struct S1;
Für Funktionstemplates kann die Template-Einführung mit Platzhaltern kombiniert werden.
Sortable{T} void f(T, auto); // long form: template<Sortable T, typename U> void f(T, U); // alternative using only placeholders: void f(Sortable, auto);
| Dieser Abschnitt ist unvollständig Grund: Template-Deklarationsseiten aktualisieren, um hierher zu verlinken |
[edit] Konzepte
Ein Konzept ist ein benannter Satz von Anforderungen. Die Definition eines Konzepts erscheint im Namensraum und hat die Form einer Funktionstemplatedefinition (in diesem Fall wird es als Funktionskonzept bezeichnet) oder einer Variablentemplatedefinition (in diesem Fall wird es als Variablenkonzept bezeichnet). Der einzige Unterschied ist, dass das Schlüsselwort concept in der decl-specifier-seq erscheint.
// variable concept from the standard library (Ranges TS) template <class T, class U> concept bool Derived = std::is_base_of<U, T>::value; // function concept from the standard library (Ranges TS) template <class T> concept bool EqualityComparable() { return requires(T a, T b) { {a == b} -> Boolean; {a != b} -> Boolean; }; }
Die folgenden Einschränkungen gelten für Funktionskonzepte:
-
inlineundconstexprsind nicht erlaubt, die Funktion ist automatischinlineundconstexpr. -
friendundvirtualsind nicht erlaubt. - Ausnahmespezifikationen sind nicht erlaubt, die Funktion ist automatisch
noexcept(true). - Kann nicht deklariert und später definiert werden, kann nicht neu deklariert werden.
- Der Rückgabetyp muss
boolsein. - Rückgabetypableitung ist nicht erlaubt.
- Die Parameterliste muss leer sein.
- Der Funktionsrumpf muss ausschließlich aus einer
return-Anweisung bestehen, deren Argument ein constraint-expression (Prädikats-Einschränkung, Konjunktion/Disjunktion anderer Einschränkungen oder ein requires-Ausdruck, siehe unten) sein muss.
Die folgenden Einschränkungen gelten für Variablenkonzepte:
- Muss den Typ
boolhaben. - Kann nicht ohne Initialisierer deklariert werden.
- Kann nicht deklariert oder im Klassenbereich deklariert werden.
-
constexprist nicht erlaubt, die Variable ist automatischconstexpr. - Der Initialisierer muss ein Constraint-Ausdruck sein (Prädikats-Einschränkung, Konjunktion/Disjunktion von Einschränkungen oder ein requires-Ausdruck, siehe unten).
Konzepte dürfen sich im Körper der Funktion oder im Initialisierer der Variablen nicht rekursiv auf sich selbst beziehen.
template<typename T> concept bool F() { return F<typename T::type>(); } // error template<typename T> concept bool V = V<T*>; // error
Explizite Instanziierungen, explizite Spezialisierungen oder partielle Spezialisierungen von Konzepten sind nicht erlaubt (die Bedeutung der ursprünglichen Definition einer Einschränkung kann nicht geändert werden).
[edit] Einschränkungen
Eine Einschränkung ist eine Sequenz logischer Operationen, die Anforderungen an Template-Argumente spezifiziert. Sie können innerhalb von requires-expressions (siehe unten) und direkt als Körper von Konzepten erscheinen.
Es gibt 9 Arten von Einschränkungen:
Die ersten drei Arten von Einschränkungen können direkt als Körper eines Konzepts oder als Ad-hoc-Requires-Klausel erscheinen.
template<typename T> requires // requires-clause (ad-hoc constraint) sizeof(T) > 1 && get_value<T>() // conjunction of two predicate constraints void f(T);
Wenn mehrere Einschränkungen an dieselbe Deklaration angehängt sind, ist die Gesamteinschränkung eine Konjunktion in der folgenden Reihenfolge: die durch template introduction eingeführte Einschränkung, Einschränkungen für jeden Template-Parameter in der Reihenfolge des Erscheinens, die requires-Klausel nach der Template-Parameterliste, Einschränkungen für jeden Funktionsparameter in der Reihenfolge des Erscheinens, nachfolgende requires-Klausel.
// the declarations declare the same constrained function template // with the constraint Incrementable<T> && Decrementable<T> template<Incrementable T> void f(T) requires Decrementable<T>; template<typename T> requires Incrementable<T> && Decrementable<T> void f(T); // ok // the following two declarations have different constraints: // the first declaration has Incrementable<T> && Decrementable<T> // the second declaration has Decrementable<T> && Incrementable<T> // Even though they are logically equivalent. // The second declaration is ill-formed, no diagnostic required template<Incrementable T> requires Decrementable<T> void g(); template<Decrementable T> requires Incrementable<T> void g(); // error
[edit] Konjunktionen
Die Konjunktion von Einschränkungen P und Q wird als P && Q spezifiziert.
// example concepts from the standard library (Ranges TS) template <class T> concept bool Integral = std::is_integral<T>::value; template <class T> concept bool SignedIntegral = Integral<T> && std::is_signed<T>::value; template <class T> concept bool UnsignedIntegral = Integral<T> && !SignedIntegral<T>;
Eine Konjunktion zweier Einschränkungen ist nur dann erfüllt, wenn beide Einschränkungen erfüllt sind. Konjunktionen werden von links nach rechts ausgewertet und kurzgeschlossen (wenn die linke Einschränkung nicht erfüllt ist, wird die Template-Argument-Substitution in die rechte Einschränkung nicht versucht: dies verhindert Fehler aufgrund von Substitution außerhalb des unmittelbaren Kontexts). Benutzerdefinierte Überladungen von operator&& sind in Constraint-Konjunktionen nicht erlaubt.
[edit] Disjunktionen
Die Disjunktion von Einschränkungen P und Q wird als P || Q spezifiziert.
Eine Disjunktion zweier Einschränkungen ist erfüllt, wenn eine der beiden Einschränkungen erfüllt ist. Disjunktionen werden von links nach rechts ausgewertet und kurzgeschlossen (wenn die linke Einschränkung erfüllt ist, wird die Template-Argument-Ableitung in die rechte Einschränkung nicht versucht). Benutzerdefinierte Überladungen von operator|| sind in Constraint-Disjunktionen nicht erlaubt.
// example constraint from the standard library (Ranges TS) template <class T = void> requires EqualityComparable<T>() || Same<T, void> struct equal_to;
[edit] Prädikats-Einschränkungen
Eine Prädikats-Einschränkung ist ein konstanter Ausdruck vom Typ bool. Sie ist nur dann erfüllt, wenn sie zu true ausgewertet wird.
template<typename T> concept bool Size32 = sizeof(T) == 4;
Prädikats-Einschränkungen können Anforderungen an Nicht-Typ-Template-Parameter und an Template-Template-Argumente spezifizieren.
Prädikats-Einschränkungen müssen direkt zu bool ausgewertet werden, keine Konvertierungen erlaubt.
template<typename T> struct S { constexpr explicit operator bool() const { return true; } }; template<typename T> requires S<T>{} // bad predicate constraint: S<T>{} is not bool void f(T); f(0); // error: constraint never satisfied
[edit] Anforderungen
Das Schlüsselwort requires wird auf zwei Arten verwendet:
template<typename T> void f(T&&) requires Eq<T>; // can appear as the last element of a function declarator template<typename T> requires Addable<T> // or right after a template parameter list T add(T a, T b) { return a + b; }
true, wenn das entsprechende Konzept erfüllt ist, und andernfalls falsch.template<typename T> concept bool Addable = requires (T x) { x + x; }; // requires-expression template<typename T> requires Addable<T> // requires-clause, not requires-expression T add(T a, T b) { return a + b; } template<typename T> requires requires (T x) { x + x; } // ad-hoc constraint, note keyword used twice T add(T a, T b) { return a + b; }
Die Syntax eines requires-expression ist wie folgt:
requires ( parameter-list(optional) ) { requirement-seq } |
|||||||||
| parameter-liste | - | eine durch Kommas getrennte Liste von Parametern wie in einer Funktionsdeklaration, außer dass Standardargumente nicht erlaubt sind und der letzte Parameter kein Ellipsis sein kann. Diese Parameter haben keinen Speicher, keine Linkage und keine Lebensdauer. Diese Parameter sind bis zum schließenden } der requirement-seq gültig. Wenn keine Parameter verwendet werden, können die runden Klammern ebenfalls weggelassen werden. |
| requirement-seq | - | eine durch Leerzeichen getrennte Sequenz von requirements, wie unten beschrieben (jede Anforderung endet mit einem Semikolon). Jede Anforderung fügt eine weitere Einschränkung zur Konjunktion von Einschränkungen hinzu, die dieser requires-Ausdruck definiert. |
Jede Anforderung in der requirements-seq ist eine der folgenden:
- einfache Anforderung
- Typ-Anforderungen
- zusammengesetzte Anforderungen
- verschachtelte Anforderungen
Anforderungen können auf die in Scope befindlichen Template-Parameter und auf die lokalen Parameter verweisen, die in der parameter-list eingeführt wurden. Wenn ein requires-Ausdruck parametrisiert ist, wird er als Einführung einer parametrisierten Einschränkung bezeichnet.
Die Substitution von Template-Argumenten in einen requires-Ausdruck kann zur Bildung ungültiger Typen oder Ausdrücke in seinen Anforderungen führen. In solchen Fällen:
- Wenn ein Substitutionsfehler in einem requires-Ausdruck auftritt, der außerhalb einer Deklaration einer templatisierten Entität verwendet wird, ist das Programm ill-formed.
- Wenn der requires-Ausdruck in einer Deklaration einer templatisierten Entität verwendet wird, wird die entsprechende Einschränkung als "nicht erfüllt" behandelt und der Substitutionsfehler ist kein Fehler, jedoch:
- Wenn ein Substitutionsfehler in einem requires-Ausdruck für alle möglichen Template-Argumente auftreten würde, ist das Programm ill-formed, keine Diagnose erforderlich.
template<class T> concept bool C = requires { new int[-(int)sizeof(T)]; // invalid for every T: ill-formed, no diagnostic required };
[edit] Einfache Anforderungen
Eine einfache Anforderung ist eine beliebige Ausdrucksanweisung. Die Anforderung ist, dass der Ausdruck gültig ist (dies ist eine Ausdruckseinschränkung). Im Gegensatz zu Prädikats-Einschränkungen findet keine Auswertung statt, es wird nur die Sprachkorrektheit geprüft.
template<typename T> concept bool Addable = requires (T a, T b) { a + b; // "the expression a+b is a valid expression that will compile" }; // example constraint from the standard library (ranges TS) template <class T, class U = T> concept bool Swappable = requires(T&& t, U&& u) { swap(std::forward<T>(t), std::forward<U>(u)); swap(std::forward<U>(u), std::forward<T>(t)); };
[edit] Typ-Anforderungen
Eine Typ-Anforderung ist das Schlüsselwort typename gefolgt von einem Typnamen, optional qualifiziert. Die Anforderung ist, dass der benannte Typ existiert (eine Typ-Einschränkung): Dies kann verwendet werden, um zu überprüfen, ob ein bestimmter benannter verschachtelter Typ existiert, oder ob ein Klassentemplate einen Typ benennt, oder ob ein Alias-Template einen Typ benennt.
template<typename T> using Ref = T&; template<typename T> concept bool C = requires { typename T::inner; // required nested member name typename S<T>; // required class template specialization typename Ref<T>; // required alias template substitution }; //Example concept from the standard library (Ranges TS) template <class T, class U> using CommonType = std::common_type_t<T, U>; template <class T, class U> concept bool Common = requires (T t, U u) { typename CommonType<T, U>; // CommonType<T, U> is valid and names a type { CommonType<T, U>{std::forward<T>(t)} }; { CommonType<T, U>{std::forward<U>(u)} }; };
[edit] Zusammengesetzte Anforderungen
Eine zusammengesetzte Anforderung hat die Form:
{ expression } noexcept(optional) trailing-return-type(optional) ; |
|||||||||
und spezifiziert eine Konjunktion der folgenden Einschränkungen:
noexcept verwendet wird, muss der Ausdruck auch noexcept sein (exception constraint)template<typename T> concept bool C2 = requires(T x) { {*x} -> typename T::inner; // the expression *x must be valid // AND the type T::inner must be valid // AND the result of *x must be convertible to T::inner }; // Example concept from the standard library (Ranges TS) template <class T, class U> concept bool Same = std::is_same<T,U>::value; template <class B> concept bool Boolean = requires(B b1, B b2) { { bool(b1) }; // direct initialization constraint has to use expression { !b1 } -> bool; // compound constraint requires Same<decltype(b1 && b2), bool>; // nested constraint, see below requires Same<decltype(b1 || b2), bool>; };
[edit] Verschachtelte Anforderungen
Eine verschachtelte Anforderung ist eine weitere requires-clause, die mit einem Semikolon abgeschlossen wird. Dies wird verwendet, um Prädikats-Einschränkungen (siehe oben) einzuführen, die in Bezug auf andere benannte Konzepte, angewendet auf die lokalen Parameter, ausgedrückt werden (außerhalb einer requires-Klausel können Prädikats-Einschränkungen keine Parameter verwenden, und das direkte Platzieren eines Ausdrucks in einer requires-Klausel macht ihn zu einer Ausdruckseinschränkung, was bedeutet, dass er nicht ausgewertet wird).
// example constraint from Ranges TS template <class T> concept bool Semiregular = DefaultConstructible<T> && CopyConstructible<T> && Destructible<T> && CopyAssignable<T> && requires(T a, size_t n) { requires Same<T*, decltype(&a)>; // nested: "Same<...> evaluates to true" { a.~T() } noexcept; // compound: "a.~T()" is a valid expression that doesn't throw requires Same<T*, decltype(new T)>; // nested: "Same<...> evaluates to true" requires Same<T*, decltype(new T[n])>; // nested { delete new T }; // compound { delete new T[n] }; // compound };
[edit] Konzept-Auflösung
Wie jedes andere Funktionstemplate kann ein Funktionskonzept (aber kein Variablenkonzept) überladen werden: Mehrere Konzeptdefinitionen können bereitgestellt werden, die alle denselben concept-name verwenden.
Die Konzept-Auflösung wird durchgeführt, wenn ein concept-name (der qualifiziert sein kann) in Folgendem vorkommt:
template<typename T> concept bool C() { return true; } // #1 template<typename T, typename U> concept bool C() { return true; } // #2 void f(C); // the set of concepts referred to by C includes both #1 and #2; // concept resolution (see below) selects #1.
Um die Konzept-Auflösung durchzuführen, werden die Template-Parameter jedes Konzepts, das mit dem Namen (und ggf. der Qualifikation) übereinstimmt, mit einer Sequenz von Konzeptargumenten abgeglichen, die Template-Argumente und Wildcards sind. Eine Wildcard kann einen Template-Parameter beliebiger Art (Typ, Nicht-Typ, Template) abgleichen. Der Argumentensatz wird je nach Kontext unterschiedlich konstruiert:
template<typename T> concept bool C1() { return true; } // #1 template<typename T, typename U> concept bool C1() { return true; } // #2 void f1(const C1*); // <wildcard> matches <T>, selects #1
template<typename T> concept bool C1() { return true; } // #1 template<typename T, typename U> concept bool C1() { return true; } // #2 void f2(C1<char>); // <wildcard, char> matches <T, U>, selects #2
template<typename... Ts> concept bool C3 = true; C3{T} void q2(); // OK: <T> matches <...Ts> C3{...Ts} void q1(); // OK: <...Ts> matches <...Ts>
template<typename T> concept bool C() { return true; } // #1 template<typename T, typename U> concept bool C() { return true; } // #2 template <typename T> void f(T) requires C<T>(); // matches #1
Die Konzept-Auflösung erfolgt durch Abgleich jedes Arguments mit dem entsprechenden Parameter jedes sichtbaren Konzepts. Standard-Template-Argumente (falls verwendet) werden für jeden Parameter instanziiert, der keinem Argument entspricht, und dann der Argumentliste angehängt. Ein Template-Parameter gleicht ein Argument nur dann ab, wenn er die gleiche Art (Typ, Nicht-Typ, Template) hat, es sei denn, das Argument ist eine Wildcard. Ein Parameter-Pack gleicht null oder mehr Argumente ab, solange alle Argumente dem Muster in der Art entsprechen (es sei denn, es sind Wildcards).
Wenn ein Argument nicht mit seinem entsprechenden Parameter übereinstimmt oder wenn es mehr Argumente als Parameter gibt und der letzte Parameter kein Pack ist, ist das Konzept nicht lebensfähig. Wenn es null oder mehr als ein lebensfähiges Konzept gibt, ist das Programm ill-formed.
template<typename T> concept bool C2() { return true; } template<int T> concept bool C2() { return true; } template<C2<0> T> struct S1; // error: <wildcard, 0> matches // neither <typename T> nor <int T> template<C2 T> struct S2; // both #1 and #2 match: error
| Dieser Abschnitt ist unvollständig Grund: Benötigt ein Beispiel mit aussagekräftigen Konzepten, nicht diese 'return true'-Platzhalter |
[edit] Partielle Ordnung von Einschränkungen
Vor jeder weiteren Analyse werden Einschränkungen normalisiert, indem der Körper jedes benannten Konzepts und jeder requires-Ausdruck substituiert wird, bis eine Sequenz von Konjunktionen und Disjunktionen über atomare Einschränkungen übrig bleibt, welche Prädikats-Einschränkungen, Ausdruckseinschränkungen, Typ-Einschränkungen, implizite Konvertierungseinschränkungen, Argumentableitungseinschränkungen und Ausnahme-Einschränkungen sind.
Ein Konzept P subsumiert ein Konzept Q, wenn bewiesen werden kann, dass P Q impliziert, ohne Typen und Ausdrücke auf Äquivalenz zu analysieren (daher impliziert N >= 0 nicht N > 0).
Insbesondere wird P zuerst in die disjunktive Normalform und Q in die konjunktive Normalform umgewandelt und dann wie folgt verglichen:
- jede atomare Einschränkung
Asubsumiert eine äquivalente atomare EinschränkungA. - jede atomare Einschränkung
Asubsumiert eine DisjunktionA||Bund subsumiert keine KonjunktionA&&B. - jede Konjunktion
A&&BsubsumiertA, aber eine DisjunktionA||Bsubsumiert nichtA.
Die Subsumptionsbeziehung definiert eine partielle Ordnung von Einschränkungen, die zur Bestimmung von Folgendem verwendet wird:
- der beste lebensfähige Kandidat für eine Nicht-Template-Funktion in der Überladungsauflösung.
- die Adresse einer Nicht-Template-Funktion in einem Überladungssatz.
- der beste Treffer für ein Template-Template-Argument.
- partielle Ordnung von Klassentemplatespezialisierungen.
- partielle Ordnung von Funktionstemplates.
| Dieser Abschnitt ist unvollständig Grund: Backlinks von oben nach hier |
Wenn Deklarationen D1 und D2 eingeschränkt sind und die normalisierten Einschränkungen von D1 die von D2 subsumieren (oder wenn D1 eingeschränkt und D2 uneingeschränkt ist), dann wird D1 als mindestens so eingeschränkt wie D2 bezeichnet. Wenn D1 mindestens so eingeschränkt wie D2 ist und D2 nicht mindestens so eingeschränkt wie D1 ist, dann ist D1 stärker eingeschränkt als D2.
template<typename T> concept bool Decrementable = requires(T t) { --t; }; template<typename T> concept bool RevIterator = Decrementable<T> && requires(T t) { *t; }; // RevIterator subsumes Decrementable, but not the other way around // RevIterator is more constrained as Decrementable void f(Decrementable); // #1 void f(RevIterator); // #2 f(0); // int only satisfies Decrementable, selects #1 f((int*)0); // int* satisfies both constraints, selects #2 as more constrained void g(auto); // #3 (unconstrained) void g(Decrementable); // #4 g(true); // bool does not satisfy Decrementable, selects #3 g(0); // int satisfies Decrementable, selects #4 because it is more constrained
[edit] Schlüsselwörter
[edit] Compiler-Unterstützung
GCC >= 6.1 unterstützt diese technische Spezifikation (erforderliche Option -fconcepts)