Was genau ist der im C++11-Standard erwähnte „unmittelbare Kontext“, für den SFINAE gilt?

Lesezeit: 7 Minuten

Was genau ist der im C11 Standard erwahnte „unmittelbare Kontext fur
Andy Prowl

Paragraph 14.8.2/8 des C++11-Standards legt die Bedingungen fest, unter denen ein Substitutionsfehler zu einem “harten” Kompilierungsfehler (wodurch die Kompilierung fehlschlägt) oder zu einem “weichen” Fehler führen soll oder nicht Veranlassen Sie den Compiler, eine Vorlage aus einer Reihe von Kandidaten für die Auflösung von Überladungen zu verwerfen (ohne dass die Kompilierung fehlschlägt und das bekannte SFINAE-Idiom aktiviert wird):

Wenn eine Ersetzung zu einem ungültigen Typ oder Ausdruck führt, schlägt die Typableitung fehl. Ein ungültiger Typ oder Ausdruck wäre falsch formatiert, wenn er mit den ersetzten Argumenten geschrieben würde. [ Note: Access checking is done as
part of the substitution process. —end note ] Nur ungültige Typen und Ausdrücke im unmittelbaren Kontext des Funktionstyps und seiner Vorlagenparametertypen können zu einem Abzugsfehler führen. […]

Die Wörter “unmittelbaren Kontext” kommen im gesamten C++11-Standard nur 8 Mal vor, und jedes Mal folgt (oder tritt als Teil davon) eine Instanz des folgenden (nicht normativen) Textes auf:

[Note: The evaluation
of the substituted types and expressions can result in side effects such as the instantiation of class template
specializations and/or function template specializations, the generation of implicitly-defined functions, etc.
Such side effects are not in the “immediate context” and can result in the program being ill-formed.
—end
note ]

Der Hinweis gibt einen (nicht sehr großzügigen) Hinweis darauf, was gemeint ist unmittelbaren Kontextaber zumindest für mich reicht das oft nicht aus, um zu entscheiden, ob eine Substitution einen “harten” Kompilierungsfehler verursachen soll oder nicht.

FRAGE:

Können Sie eine Erklärung, ein Entscheidungsverfahren und/oder einige konkrete Beispiele geben, um herauszufinden, in welchen Fällen ein Substitutionsfehler in der “unmittelbaren Kontext” des Funktionstyps und seiner Vorlagenparametertypen?

Was genau ist der im C11 Standard erwahnte „unmittelbare Kontext fur
Jonathan Wakely

Wenn Sie alle Vorlagen und implizit definierten Funktionen berücksichtigen, die zum Bestimmen des Ergebnisses der Vorlagenargumentsubstitution erforderlich sind, und sich vorstellen, dass sie zuerst generiert werden, bevor die Ersetzung beginnt, dann stehen alle Fehler, die in diesem ersten Schritt auftreten, nicht im unmittelbaren Kontext. und zu harten Fehlern führen.

Wenn all diese Instanziierungen und impliziten Definitionen (die das Definieren von Funktionen als gelöscht enthalten können) fehlerfrei durchgeführt werden können, dann werden alle weiteren “Fehler”, die während der Substitution auftreten (dh beim Verweisen auf die instanziierten Vorlagen und implizit definierten Funktionen in den Funktionsvorlagen Unterschrift) sind keine Fehler, sondern führen zu Abzugsfehlern.

Also gegeben eine Funktionsvorlage wie diese:

template<typename T>
void
func(typename T::type* arg);

und ein “Fallback”, das verwendet wird, wenn der Abzug für die andere Funktion fehlschlägt:

template<typename>
void
func(...);

und eine Klassenvorlage wie diese:

template<typename T>
  struct A
  {
    typedef T* type;
  };

Ein Anruf bei func<A<int&>>(nullptr) wird ersetzen A<int&> zum T und um zu prüfen, ob T::type existiert, muss es instanziiert werden A<int&>. Stellen wir uns vor, eine explizite Instanziierung vor den Aufruf von zu setzen func<A<int&>(nullptr):

template class A<int&>;

dann würde das fehlschlagen, weil es versucht, den Typ zu erstellen int&* und Verweise auf Referenzen sind nicht erlaubt. Wir kommen nicht zu dem Punkt, an dem wir prüfen, ob die Substitution erfolgreich ist, weil es einen schwerwiegenden Fehler bei der Instanziierung gibt A<int&>.

Nehmen wir nun an, es gibt eine explizite Spezialisierung von A:

template<>
  struct A<char>
  {
  };

Ein Anruf bei func<A<char>>(nullptr) erfordert die Instanziierung von A<char>stellen Sie sich also eine explizite Instanziierung irgendwo im Programm vor dem Aufruf vor:

template class A<char>;

Diese Instanziierung ist in Ordnung, es gibt keinen Fehler, also fahren wir mit der Argumentsubstitution fort. Die Instantiierung von A<char> hat funktioniert, aber A<char>::type existiert nicht, aber das ist in Ordnung, weil es nur in der Deklaration von referenziert wird funcführt also nur dazu, dass der Argumentabzug fehlschlägt, und der Fallback ... Funktion wird stattdessen aufgerufen.

In anderen Situationen kann die Substitution dazu führen, dass spezielle Elementfunktionen implizit definiert werden, möglicherweise als gelöscht, was andere Instanziierungen oder implizite Definitionen auslösen kann. Wenn Fehler während dieser Phase “Generieren von Instanziierungen und impliziten Definitionen” auftreten, dann sind sie Fehler, aber wenn dies gelingt, aber während der Ersetzung erweist sich ein Ausdruck in der Signatur der Funktionsvorlage als ungültig, z. B. weil sie ein nicht vorhandenes Element verwendet oder Etwas, das implizit als gelöscht definiert wurde, ist kein Fehler, sondern nur ein Deduktionsfehler.

Das mentale Modell, das ich verwende, ist also, dass die Substitution zuerst einen “Vorbereitungsschritt” durchführen muss, um Typen und Member zu generieren, was schwerwiegende Fehler verursachen kann, aber sobald wir alle erforderlichen Generierungen durchgeführt haben, sind alle weiteren ungültigen Verwendungen keine Fehler. All dies bewirkt natürlich nur, dass das Problem von „was tut unmittelbaren Kontext meinst?” bis “Welche Typen und Member müssen generiert werden, bevor diese Ersetzung überprüft werden kann?”, also kann es Ihnen helfen oder auch nicht!

  • Vielen Dank für die sehr ausführliche und ausführliche Erklärung

    – Andy Prowl

    7. März 2013 um 1:07 Uhr

  • Es ist jedoch keineswegs maßgeblich, es ist nur mein mentales Modell, das sich ständig weiterentwickelt hat und regelmäßig überarbeitet werden musste, wenn es mir nicht gelang!

    – Jonathan Wakely

    7. März 2013 um 1:09 Uhr

  • Ich finde es hilfreich, es spielt keine Rolle, ob es nicht normativ ist, solange es meine Perspektive bereichert

    – Andy Prowl

    7. März 2013 um 1:15 Uhr

  • @AndyProwl: Siehe dieses etwas verwandte Thema: SFINAE, Deduktion vs. Instanziierung

    – Nawaz

    17. März 2013 um 17:58 Uhr

  • @Nawaz: Danke 🙂 Ich hatte diese Fragen und Antworten gefunden, war mir aber nicht sicher, was die formale Bedeutung von “unmittelbarer Kontext” war. Ich glaube immer noch, dass dies im Standard formeller definiert werden sollte, aber Jonathans Antwort gibt eine gute Erklärung

    – Andy Prowl

    17. März 2013 um 18:05 Uhr

Die unmittelbaren Kontext ist im Grunde das, was Sie in der Vorlagendeklaration selbst sehen. Alles darüber hinaus ist ein harter Fehler. Beispiele für schwere Fehler:

#include <type_traits>

template<class T>
struct trait{ using type = typename T::type; };

template<class T, class U = typename trait<T>::type>
void f(int);
void f(...);

template<class T, class U = typename T::type>
void g(int);
void g(...);

template<class>
struct dependent_false : std::false_type{};

template<class T>
struct X{
    static_assert(dependent_false<T>(), "...");
    using type = void;
};

int main(){
    f<int>(0);
    g<X<int>>(0);
}

Live-Version.

  • Ich versuche meine Frage anhand eines Beispiels etwas besser zu verdeutlichen. Warum ist Das ein weicher Fehler? Warum wird es als im “unmittelbaren Kontext” betrachtet, obwohl der Fehler beim Instanziieren in einem verschachtelten Kontext verursacht wird T2<int>?

    – Andy Prowl

    7. März 2013 um 0:43 Uhr


  • @Andy: Weil es ist nicht. 😉 ... akzeptiert im Gegensatz zur anderen Überladung auch null Argumente.

    – Xeo

    7. März 2013 um 0:45 Uhr


  • Vorlagenaliase werden auch als “unmittelbarer Kontext” betrachtet. Aus der Norm geht das nicht hervor, aber laut einem Komiteemitglied ist das beabsichtigt. Sehen groups.google.com/group/comp.std.c++/msg/c075b74ae0b052f2

    – Sellibitze

    7. März 2013 um 13:14 Uhr


  • @sellibitze: Ja, richtig, da die Verwendung von Aliasen im Grunde Makros auf Vorlagenebene sind (zumindest denke ich so über sie).

    – Xeo

    7. März 2013 um 13:16 Uhr

  • @ustulation: Nein, wenn du das weglässt <int> Teil, können Sie nicht die erste Überladung von aufrufen f mehr – beachten Sie das T wird nicht abgeleitet, es hat ausdrücklich bereitzustellen. Als solches wird offensichtlich die variadische Überladung gewählt.

    – Xeo

    2. November 2014 um 9:33 Uhr


964680cookie-checkWas genau ist der im C++11-Standard erwähnte „unmittelbare Kontext“, für den SFINAE gilt?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy