Warum findet ADL keine Funktionsvorlagen?
-
22-10-2019 - |
Frage
Welchen Teil der C ++ - Spezifikation beschränkt sich auf die Argument -abhängige Suche in der Suche nach Funktionsvorlagen in der Menge der zugehörigen Namespaces? Mit anderen Worten, warum ruft der letzte Anruf ein? main
unten nicht kompilieren?
namespace ns {
struct foo {};
template<int i> void frob(foo const&) {}
void non_template(foo const&) {}
}
int main() {
ns::foo f;
non_template(f); // This is fine.
frob<0>(f); // This is not.
}
Lösung
Dieser Teil erklärt es:
C ++ Standard 03 14.8.1.6:
Hinweis: Für einfache Funktionsnamen gilt die Argument -abhängige Suche (3.4.2) auch dann, wenn der Funktionsname im Rahmen des Aufrufs nicht sichtbar ist. Dies liegt daran, dass der Anruf still die syntaktische Form eines Funktionsaufrufs (3.4.1) hat. Wenn jedoch eine Funktionsvorlage mit expliziten Vorlagenargumenten verwendet wird, hat der Aufruf nicht das richtige syntaktische Formular, es sei denn, es gibt eine Funktionsvorlage, bei der dieser Name am Punkt des Aufrufs sichtbar ist. Wenn kein solcher Name sichtbar ist, ist der Anruf nicht syntaktisch gut geformt und es gilt nicht argumentabhängig. Wenn ein solcher Name sichtbar ist, gilt die argumentabhängige Suche und zusätzliche Funktionsvorlagen finden Sie in anderen Namensspitzen.
namespace A {
struct B { };
template<int X> void f(B);
}
namespace C {
template<class T> void f(T t);
}
void g(A::B b) {
f<3>(b); //ill-formed: not a function call
A::f<3>(b); //well-formed
C::f<3>(b); //ill-formed; argument dependent lookup
// applies only to unqualified names
using C::f;
f<3>(b); //well-formed because C::f is visible; then
// A::f is found by argument dependent lookup
}
Andere Tipps
Ich möchte eine leicht akzeptierte Antwort verfeinern. In der OP -Frage ist nicht klar, aber der wichtige Teil des Standards (zitiert von Kornel) ist dies (Hervorhebung Mine):
Aber wenn eine Funktionsvorlage mit Explizite Vorlagenargumente wird verwendet, der Anruf hat nicht die richtige syntaktische Form
Was verboten ist, stützt sich also auf ADL und verwendet explizite Vorlagenargumente. Die Verwendung von Argumenten für nicht-type-Vorlagen erfordert leider die Verwendung explizite Argumente (es sei denn, sie haben Standardwerte).
Im Folgenden finden Sie einen Beispielcode .:::
#include <string>
#include <utility>
namespace C {
struct B { };
template<class T> void f(T t){}
}
void g(C::B b) {
f(b); // OK
//f<C::B>(b); // ill-formed: not a function call, but only
// because explicit template argument were used
std::string s;
move(s); // OK
//move<std::string&>(s); // Error, again because
// explicit template argument were used
std::move<std::string&>(s); // Ok
}
int main()
{
C::B b;
g(b);
}
Seit C ++ 20 arbeitet ADL auch gut mit explizite Funktionsvorlage. Hier ist der Vorschlag:P0846R0: ADL- und Funktionsvorlagen, die nicht sichtbar sind:
Anstatt dass der Benutzer das Schlüsselwort der Vorlage verwenden kann, wurde eine Überarbeitung der Suchregeln vorgeschlagen, so dass ein Name, für den ein normales Suchanlagen entweder kein Ergebnis erbringt oder eine oder mehrere Funktionen findet und von AA "<" folgt, als Wenn ein Funktionsvorlagenname gefunden worden wäre und ADL ausführen würde.
Derzeit hat nur GCC 9 diese Funktion implementiert, sodass Ihr Beispiel kompilieren kann.
Bearbeiten: Nein, das ist nicht richtig. Sehen @Kornels Antwort.
Ich bin mir nicht ganz sicher, aber nachdem ich Strousstrups "The C ++ Programmiersprache" konsultiert habe, denke ich, dass Anhang C Abschnitt 13.8.4 könnte die Ursache sein.
Seit frob
ist eine Vorlage, für die man sie möglicherweise spezialisiert hat? i=0
An einem Punkt, nachdem Sie es angerufen haben. Dies bedeutet, dass die Implementierung zwei mögliche Möglichkeiten zur Auswahl dessen erhalten würde, welche frob
Um so zu rufen, wie es erscheint, kann es es am Zeitpunkt der Instanziierung wählen oder Am Ende der Verarbeitung der Übersetzungseinheit.
Ich denke, das Problem ist, dass Sie es tun könnten
namespace ns {
struct foo {};
template<int i> void frob(foo const&) {}
}
int main() {
ns::foo f;
frob<0>(f);
return 0;
}
namespace ns {
template<> void frob< 0 >(foo const&) { /* Do something different*/ }
}