Wie man die vorgeschlagene Auflösung von # 1664 versteht

8

Nachdem ich mir die vorgeschlagene Auflösung von # 1664 ( vorgeschlagene Auflösung 1664 ) angesehen habe, bin ich verwirrt über die Regeln eines Standardarguments einer Funktionsvorlage. Zitieren Sie den Inhalt hier:

Gemäß 8.1.5 [expr.prim.lambda] Absatz 3

Der Schließungstyp wird im kleinsten Blockbereich, Klassenbereich oder Namespace-Bereich deklariert, der den entsprechenden Lambda-Ausdruck enthält. [Hinweis: Hiermit wird die Anzahl der Namespaces und Klassen festgelegt, die dem Schließungstyp zugeordnet sind (6.4.2 [basic.lookup.argdep]). Die Parametertypen eines Lambda-Deklarators wirken sich nicht auf diese zugeordneten Namespaces und Klassen aus. - Endnote]

In 17.8.1 [temp.inst] Absatz 13 heißt es jedoch

Wenn eine Funktionsvorlage f so aufgerufen wird, dass ein Standardargument verwendet werden muss, werden die abhängigen Namen nachgeschlagen, die Semantikbeschränkungen überprüft und die Instanziierung einer im Standardargument verwendeten Vorlage erfolgt wie das Standardargument war ein Initialisierer, der in einer Funktionsvorlagenspezialisierung mit demselben Bereich, denselben Vorlagenparametern und demselben Zugriff wie der zu diesem Zeitpunkt verwendete Funktionsvorlagen f verwendet wurde.

Eine Möglichkeit besteht also darin, dass der Schließungstyp für den Lambda-Ausdruck in einem Standardargument für eine Vorlagenfunktion (oder vermutlich eine Elementfunktion einer Klassenvorlage) als in einem Blockbereich im Hauptteil von deklariert betrachtet wird die Spezialisierung auf fiktive Funktionsvorlagen.

Betrachten Sie das folgende Beispiel:

 namespace J {
    inline namespace K {
      template <typename T> int zap(const T &t) { foo(t); return 0; }
      template <typename T> void zip(int = zap([] { })) { }
    }
    template <typename T> void foo(const T &) { }
  }
  void bar() { 
    J::K::zip<long>(); 
    /*Accroding to the above wording,the invoke just like:  
      => J::K::zip<long>(zap([] { })); 
   */
  }

Wenn zip keine Vorlage war, löst die argumentabhängige Suche die Suche nach foo in allen getesteten Implementierungen erfolgreich auf. Es gibt jedoch Implementierungsabweichungen bei der Behandlung des Beispiels wie geschrieben.

Vorgeschlagener Beschluss (September 2013):

Ändern Sie 17.8.1 [temp.inst] Absatz 13 wie folgt:
Wenn eine Funktionsvorlage f so aufgerufen wird, dass ein Standardargument verwendet werden muss, werden die abhängigen Namen nachgeschlagen, die Semantikbeschränkungen überprüft und die Instanziierung von Jede im Standardargument verwendete Vorlage wird so ausgeführt, als wäre das Standardargument ein Initialisierer, der in einer Funktionsvorlagenspezialisierung mit demselben Bereich, denselben Vorlagenparametern und demselben Zugriff wie die zu diesem Zeitpunkt verwendete Funktionsvorlage f verwendet wurde, außer dass der Bereich, in dem ein Schließungstyp deklariert wird (8.1.5 [expr.prim.lambda]) - und damit die zugehörigen Namespaces - wie aus dem Kontext der Definition für das Standardargument bestimmt bleibt. Diese Analyse wird als Standardargumentinstanziierung bezeichnet. Das instanziierte Standardargument wird dann als Argument von f verwendet.

Beachten Sie den betonten Teil, wenn ich nicht falsch verstehen, es bedeutet , wenn der betonte Teil Kommentar aus, die foonicht nachgeschlagen werden könnte Argument abhängig Look , weil das Argument [] { }die Namespace ist weder Jnoch K, die Form annehmen , innerhalb function barwie J::K::zip<long>(zap([] { }) /*default argument*/);, So accroding [ expr.prim.lambda] Absatz 3 der Namensraum von [] { }zumin ist fuction barund in diesem Umfang, gibt es keine foogefunden werden kann, also ist das betonte Teil für diesen Fall ist , die den Namespace betrachten [] { }innerhalb zapals die selben wie zap, bedeutet dies , den Namespace [] { }ist K, Jetzt foobefindet sich das im übergeordneten NamespaceJdurch das Argument abhängige Lookup Regeln Bisher Wenn ich misundertand diese Regeln, korrigieren Sie bitte me.the andere Punkt Ansicht ist , dass das Standardargument jedes Mal ausgewertet, wenn die Funktion aufgerufen, auch wenn der Standard ist nicht abhängig .So weiterhin prüft die folgenden Code:

#include <iostream>
struct A {

};
template<typename T>
int func(T, float) {  //#a
    std::cout << "float" << std::endl;
    return 0;
}
template<typename T>
void test(int = func(A{}, 0)) { //#1

}
template<typename T>
int func(T, int) {  //#b
    std::cout << "int" << std::endl;
    return 0;
}
int main() {
    test<A>(); //#2 transform to: test<A>(func(A{}, 0)); here,#b should be the best match
    std::getchar();
}

Obwohl das Standardargument funcnicht abhängig ist, sollte es jedoch jedes Mal bestimmt werden, wenn die Funktion testaufgerufen wird, und ich teste den Code in einigen Kompatibilitäten.
Die Version von MSVC Bericht „int“, gcc Bericht „float“, Klirren Bericht „float“ Alles, was zum Teufel ist? Auf den Bericht des gcc oder Klirren Accroding, um es in die scheint funcbestimmt an #1und MSVC bewiesen , dass die funcbestimmt wird , bei #2. Wenn MSVC falsch ist, bedeutet dies, dass das nicht abhängige Standardargument innerhalb von # 1 bestimmt werden kann und es nicht erforderlich ist, jedes Mal zu bestimmen, wenn die Funktion aufgerufen wird, warum der hervorgehobene Teil hinzugefügt werden muss.Wenn ich den hervorgehobenen Teil richtig verstehe, besteht der Zweck darin, den Namespace des Schließungstyps innerhalb des Standardarguments konsistent zu halten, unabhängig davon, ob sich der Lambda-Ausdruck am Punkt der Funktionsdeklaration oder am Aufrufpunkt befindet . Wenn ich diese Regeln falsch verstehe, wie kann ich sie richtig interpretieren?

AKTUALISIEREN:

Die Version 9.1 oder höher von gcc kann den in # 1664 genannten Code nicht erfüllen. Sie meldet einen Fehler ( das Ergebnis entspricht ).

Fragen:

1. Muss das nicht abhängige Standardargument der Funktionsvorlage oder der Nichtvorlagenfunktion jedes Mal ermittelt werden, wenn die entsprechende Funktion aufgerufen wird?

2.Was bedeutet "die Definition für das Standardargument"? Ist dieser Wortlaut streng? ( Mit anderen Worten, nach meinem Verständnis möchten die hinzugefügten Regeln ausdrücken, dass der Namespace des Closeure-Typs der einer Funktionsdeklaration ist, in der enthält ein Standardargument, das den entsprechenden Lambda-Ausdruck enthält, nicht wahr? wenn mein Verständnis über die falsch ist, korrigieren Sie mich )

Buchse X.
quelle
1
"Gleicher Umfang ... wie der der verwendeten Funktionsvorlage f" klingt tatsächlich so, als würde die hypothetische Spezialisierung innerhalb des Namespace liegen J::K, nicht innerhalb der Definition von bar, aber dann sehe ich nicht, wie die geänderte Phrase einen Unterschied machen würde.
Aschepler
Ich verstehe das alles, aber ich glaube nicht, dass wir das Gleiche aus dem Wortlaut vor dem geänderten / fettgedruckten Teil geschlossen haben.
Aschepler
1
"Jetzt starte ich ein Kopfgeld für zusätzliche Fragen." So funktioniert StackOverflow nicht. Wenn Sie eine neue Frage haben, stellen Sie eine neue Frage. Eine Frage pro Frage. Ich habe Ihre Bearbeitung zurückgesetzt.
Barry
@Barry Ich denke, die aktualisierte Frage gehört zu "Wie man die vorgeschlagene Auflösung von # 1664 versteht"
Jack X
Entschuldigung, Sie müssen neue Fragen stellen . Der Stapelüberlauf dient dazu, ein Repository mit nützlichen Fragen und deren Antworten für zukünftige Besucher zu erstellen. Nachdem Ihre erste Frage eine Antwort enthält, betrachten wir diese Seite als ein Ganzes. Bitte respektieren Sie die Arbeit, die der Antwortende in die Bereitstellung einer Lösung gesteckt hat, und stellen Sie neue Fragen in neue Fragen. Das Ändern (Erweitern) des Problems hier würde andernfalls die Antwort ungültig machen und Ihren Beitrag zum Thema machen (geschlossen, da "mehr Fokus benötigt").
Martijn Pieters

Antworten:

3

Standardargumente werden bei jedem Aufruf ausgewertet. Dies ist jedoch eine Laufzeit-Eigenschaft: Aufrufe werden nicht nach Quellzeile, sondern nach tatsächlichem Kontrollfluss gezählt. Getrennt davon wird ein Standardargument für eine Template - Funktion als eine Definition sein und wird instanziiert , wenn nötig, bis zu einmal pro Spezialisierung der Funktion (mit dem üblichen Vorbehalt über mehrere Punkte von Instanziierung , die zustimmen müssen). CWG1664 war ein sehr enges Thema, basierend auf dem Wortlaut dieser Instanziierung : Durch die Einführung einer fiktiven Funktionsvorlage wurde die Möglichkeit offen gelassen, dass sich die Erklärung des Lambdas „physisch“ bewegte. Das Update betrifft wirklich nur ADL.

Ihr funcBeispiel zeigt stattdessen die üblichen Regeln für die Namenssuche in Vorlagen: Egal wie oft und woher test das Standardargument instanziiert wird, funces ist kein abhängiger Name und wird daher func(T,float)(jedes Mal) gefunden. MSVC hat diese Regel bekanntermaßen nie korrekt implementiert (da ihre Implementierung fairerweise vor der Regel erfolgt und sie erst kürzlich mit der erforderlichen (und fast vollständigen) Neufassung ihrer Vorlagenunterstützung begonnen haben).

In der Zwischenzeit ist das aktuelle GCC mit dem CWG1664-Beispiel eindeutig fehlerhaft: Beachten Sie, dass es sich über foodie Verwendung beschwert , aber nicht definiert ist, was sowohl der deutlich sichtbaren { }als auch der vorherigen Fehlermeldung widerspricht , dass es nicht gefunden wurde.

Davis Herring
quelle
Ich versuche mein Verständnis über Ihre Antworten zusammenzufassen.1. Wenn es sich bei dem Standardargument um nicht abhängige Namen handelt, werden die Namen wie an diesem Punkt geschrieben gebunden (wie in [dcl.fct.default] / 5 angegeben). Stattdessen sind die abhängigen Namen erforderlich, um den Namen bei jeder Funktion nachzuschlagen namens. 2.Für einen Lambda-Ausdruck, da es sich nicht um einen Namen handelt, kann er auch dann nicht gebunden werden, wenn das Standardargument nicht abhängig ist. Wenn also kein hervorgehobener Teil der Einschränkung vorhanden ist, weist der Closeure-Typ einen anderen Namespace auf, der dem Punkt entspricht of call wo der Namespace es abdeckt. Stimmen Sie zu?
Jack X
Eine weitere Frage aus Ihren Antworten, die ich einzeln beantworten muss, lautet: Bezieht sich "Spezialisierung auf fiktive Funktionsvorlagen" auf "J :: K :: zip <long> ();" und nicht auf die Definition von "J :: K :: zip" <long> () "zum Zeitpunkt der Instanziierung?
Jack X
@jackX: Es ist das und [temp.nondep] / 1 für nicht abhängige Namen; abhängige Namen werden bei jeder Instanziierung nachgeschlagen, unabhängig davon, ob sie in einem Standardargument enthalten sind oder nicht. Der Schließungstyp würde zur Spezialisierung der fiktiven Funktionsvorlage gehören (die weder ein Aufruf noch eine tatsächliche Definition von ist zip).
Davis Herring
Die "Spezialisierung auf fiktive Funktionsvorlagen" bezieht sich also auf etwas wie "J :: K :: zip <long> ();"? und was ist der Effekt von "außer dass der Umfang, in dem ein Schließungstyp deklariert wird", ich denke, der Satz folgte ihm: "und daher die zugehörigen Namespaces - bleiben wie aus dem Kontext der Definition für das Standardargument bestimmt" ausreichend zu ADL
jack X
@jackX: Ich habe nur gesagt, dass es nicht so ist. Der Geltungsbereich ist die eigentliche Regel: Die Namespaces sind eine Folge, auch wenn sie den gesamten Import enthalten.
Davis Herring