Warum findet ADL keine Funktionsvorlagen?

83

Welcher Teil der C ++ - Spezifikation verhindert, dass die argumentabhängige Suche nach Funktionsvorlagen in den zugehörigen Namespaces sucht? Mit anderen Worten, warum kann der letzte Aufruf mainunten nicht kompiliert werden?

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.
}
Hugh
quelle
Bedeutet das, dass Sie erwarten, frob () zu arbeiten, ohne ns :: frob () zu schreiben?
Simon
Ja, in der Art einer Nicht-Vorlagenfunktion.
Hugh
Zu Ihrer Information, der obige Code schlägt auch in Comeau fehl: comeaucomputing.com/tryitout - Hinzufügen using namespace ns;oder die ns::Kompilierung besteht die Kompilierung. Das ist eine gute Frage.
fbrereto
3
@ Huw: nur von ihm gebissen :) Komisch, wie explizite Qualifikation ADL ausschließt Ich denke: /
Matthieu M.
1
@ Matt: Haha, und ich auch gerade. Kleine Programmierwelt.
GManNickG

Antworten:

86

Dieser Teil erklärt es:

C ++ Standard 03 14.8.1.6 :

[Hinweis: Für einfache Funktionsnamen gilt die argumentabhängige Suche (3.4.2) auch dann, wenn der Funktionsname im Rahmen des Aufrufs nicht sichtbar ist. Dies liegt daran, dass der Aufruf immer noch die syntaktische Form eines Funktionsaufrufs hat (3.4.1). Wenn jedoch eine Funktionsvorlage mit expliziten Vorlagenargumenten verwendet wird, hat der Aufruf nicht die richtige syntaktische Form, es sei denn, am Aufrufpunkt ist eine Funktionsvorlage mit diesem Namen sichtbar. Wenn kein solcher Name sichtbar ist, ist der Aufruf syntaktisch nicht wohlgeformt und die argumentabhängige Suche gilt nicht. Wenn ein solcher Name sichtbar ist, gilt die argumentabhängige Suche, und zusätzliche Funktionsvorlagen können in anderen Namespaces gefunden werden.

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
}
Kornel Kisielewicz
quelle
9
Was ist der Grund dafür? Scheint eine seltsame Anforderung zu sein. Ich meine, was hat die syntaktische Form mit irgendetwas zu tun?
Leichtigkeitsrennen im Orbit
22
@LightnessRacesinOrbit Abschnitt 9.3.5 in Vandevoorde & Josuttis erklärt , warum dies ist ein syntaktisches Problem (zu OPs Beispiel angenommen Namensgebung): „Ein Compiler kann das nicht entscheiden , f<3>(b)ist ein Argument Funktionsaufruf , bis er entschieden hat , dass <3>eine Liste Template - Arguments ist umgekehrt wir. Ich kann nicht entscheiden, ob <3>es sich um eine Vorlagenargumentliste handelt, bis wir festgestellt haben f(), dass es sich um eine Vorlage handelt. Da dieses Henne-Ei-Problem nicht gelöst werden kann, wird der Ausdruck als analysiert (f<3)>(b), was keinen Sinn ergibt . " Beachten Sie, dass dies der templateDisambiguierungssyntax für Vorlagen für Elementfunktionen ähnelt .
TemplateRex
9
Gibt es einen Vorschlag zur Behebung dieses Problems? template f<3>(b)kann eine bessere Syntax sein?
Balki
1
@AngelusMortis Ausdrucksausdrücke ( ... ) (beachten Sie das Fehlen eines Operators vor der Klammer) ist jedoch immer ein Funktionsaufruf, und der Compiler weiß das, sobald er die geöffnete Klammer sieht. Das Problem hierbei ist, dass <dies sowohl als Operator als auch als Anfang einer Vorlagenargumentliste dienen kann und der Compiler eine Menge zusätzlicher Analysen durchführen muss, um herauszufinden, welche (und möglicherweise gibt es einige Codeanordnungen, bei denen dies nicht möglich ist eindeutig zu tun). Es scheint, dass die Standardautoren beschlossen haben, dies illegal zu machen, vielleicht um die Haare der Compiler-Entwickler zu retten.
Miral
2
Diese Anforderung wurde in C ++ 20 aufgehoben und der Code von OP ist jetzt wohlgeformt :)
Rakete1111
7

Seit c ++ 20 funktioniert adl auch mit expliziten Funktionsvorlagen. Hier ist der Vorschlag: P0846R0 : ADL- und Funktionsvorlagen, die nicht sichtbar sind :

Anstatt den Benutzer zur Verwendung des Schlüsselworts template zu verpflichten, wurde eine Überarbeitung der Suchregeln vorgeschlagen, sodass ein Name, für den eine normale Suche entweder kein Ergebnis liefert oder eine oder mehrere Funktionen findet und dem ein aa "<" folgt, als behandelt wird Wenn ein Funktionsvorlagenname gefunden wurde und die Ausführung von ADL bewirken würde.

Derzeit hat nur GCC 9 diese Funktion implementiert, sodass Ihr Beispiel kompiliert werden kann.

live demo.

陳 力
quelle
5

Ich möchte die leicht akzeptierte Antwort verfeinern. Es ist in der OP-Frage nicht klar, aber der wichtige Teil des Standards (zitiert von Kornel) ist folgender (Schwerpunkt Mine):

Wenn jedoch eine Funktionsvorlage mit expliziten Vorlagenargumenten verwendet wird, hat der Aufruf nicht die richtige syntaktische Form

Was also verboten ist, ist sich auf ADL zu verlassen und explizite Vorlagenargumente zu verwenden. Leider erfordert die Verwendung von Vorlagenargumenten, die nicht vom Typ sind, die Verwendung expliziter Argumente (es sei denn, sie haben Standardwerte).

Unten finden Sie einen Beispielcode, der dies zeigt:

[Leben]

#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);
}
marcinj
quelle
0

Edit: Nein, das ist nicht richtig. Siehe @ Kornels Antwort .


Ich bin nicht ganz sicher , aber Stroustrup „The C ++ Programmiersprache“ Ich denke , dass 13.8.4 Anhang C Abschnitt Konsultation könnte die Ursache sein.

Da frobes sich um eine Vorlage handelt, kann man sie möglicherweise i=0zu einem Zeitpunkt spezialisieren, nachdem Sie sie aufgerufen haben. Dies bedeutet, dass der Implementierung zwei Möglichkeiten zur Auswahl übrig bleiben, welche frobaufgerufen werden soll, wie es scheint. Sie kann sie zum Zeitpunkt der Instanziierung oder am Ende der Verarbeitung der Übersetzungseinheit auswählen .

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*/ }
}
Troubadour
quelle
1
Nein, nehmen Sie die Namespaces und Sie haben immer noch Ihr Problem, nicht wahr? Die Spezialisierung nach der Verwendung ist ein normales Problem in C ++. Das spezielle Formular wird nicht verwendet, wenn es danach deklariert wird.
Kornel Kisielewicz
@Kornel: Ah ja, das gibt einen anderen Fehler, einen mehr in Übereinstimmung mit dem, was ich beschrieben habe. Fair genug, danke, dass Sie darauf hingewiesen haben.
Troubadour