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 foo
nicht nachgeschlagen werden könnte Argument abhängig Look , weil das Argument [] { }
die Namespace ist weder J
noch K
, die Form annehmen , innerhalb function bar
wie J::K::zip<long>(zap([] { }) /*default argument*/);
, So accroding [ expr.prim.lambda] Absatz 3 der Namensraum von [] { }
zumin ist fuction bar
und in diesem Umfang, gibt es keine foo
gefunden werden kann, also ist das betonte Teil für diesen Fall ist , die den Namespace betrachten [] { }
innerhalb zap
als die selben wie zap
, bedeutet dies , den Namespace [] { }
ist K
, Jetzt foo
befindet sich das im übergeordneten NamespaceJ
durch 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 func
nicht abhängig ist, sollte es jedoch jedes Mal bestimmt werden, wenn die Funktion test
aufgerufen 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 func
bestimmt an #1
und MSVC bewiesen , dass die func
bestimmt 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 )
quelle
f
" klingt tatsächlich so, als würde die hypothetische Spezialisierung innerhalb des Namespace liegenJ::K
, nicht innerhalb der Definition vonbar
, aber dann sehe ich nicht, wie die geänderte Phrase einen Unterschied machen würde.Antworten:
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
func
Beispiel zeigt stattdessen die üblichen Regeln für die Namenssuche in Vorlagen: Egal wie oft und wohertest
das Standardargument instanziiert wird,func
es ist kein abhängiger Name und wird daherfunc(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
foo
die Verwendung beschwert , aber nicht definiert ist, was sowohl der deutlich sichtbaren{ }
als auch der vorherigen Fehlermeldung widerspricht , dass es nicht gefunden wurde.quelle
zip
).