Bis zum C ++ 20-Standard von C ++, als wir einen Operator außerhalb der Klasse definieren wollten, der einige private Mitglieder einer Vorlagenklasse verwendet, verwendeten wir ein ähnliches Konstrukt:
template <typename T>
class Foo;
template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs);
template <typename T>
class Foo {
public:
constexpr Foo(T k) : mK(k) {}
constexpr friend bool operator==<T>(T lhs, const Foo& rhs);
private:
T mK;
};
template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs) {
return lhs == rhs.mK;
}
int main() {
return 1 == Foo<int>(1) ? 0 : 1;
}
Seit C ++ 20 können wir jedoch die Deklaration außerhalb der Klasse und damit auch die Vorwärtsdeklaration weglassen, sodass wir mit nur:
template <typename T>
class Foo {
public:
constexpr Foo(T k) : mK(k) {}
constexpr friend bool operator==<T>(T lhs, const Foo& rhs);
private:
T mK;
};
template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs) {
return lhs == rhs.mK;
}
Meine Frage ist nun, welcher Teil von C ++ 20 erlaubt es uns, dies zu tun? Und warum war dies in früheren C ++ - Standards nicht möglich?
Wie in den Kommentaren erwähnt, akzeptiert clang diesen in der Demo dargestellten Code nicht, was darauf hindeutet, dass dies tatsächlich ein Fehler in gcc ist.
Ich habe einen Fehlerbericht über gccs Bugzilla eingereicht
"c string" == Foo<std::string>("foo")
)).Antworten:
GCC hat einen Fehler.
Die Namenssuche wird immer für Vorlagennamen durchgeführt, die vor a stehen
<
, auch wenn der betreffende Name der Name ist, der in einer Deklaration (Freund, explizite Spezialisierung oder explizite Instanziierung) deklariert wird.Da der Name
operator==
in der Freundesdeklaration ein nicht qualifizierter Name ist und der Namenssuche in einer Vorlage unterliegt, gelten die Regeln für die zweiphasige Namenssuche. In diesem Zusammenhangoperator==
handelt es sich nicht um einen abhängigen Namen (er ist nicht Teil eines Funktionsaufrufs, daher gilt ADL nicht). Daher wird der Name an der Stelle nachgeschlagen und gebunden, an der er angezeigt wird (siehe [temp.nondep], Absatz 1). Ihr Beispiel ist schlecht geformt, weil diese Namenssuche keine Deklaration von findetoperator==
.Ich würde erwarten, dass GCC dies im C ++ 20-Modus aufgrund von P0846R0 akzeptiert , wodurch (beispielsweise)
operator==<T>(a, b)
die Verwendung in einer Vorlage ermöglicht wird, auch wenn keine vorherige Deklarationoperator==
als Vorlage sichtbar ist.Hier ist ein noch interessanterer Testfall:
Mit
-DWRONG_DECL
stimmen GCC und Clang darin überein, dass dieses Programm schlecht formuliert ist: Die uneingeschränkte Suche nach der Freundesdeklaration Nr. 2 findet im Kontext der Vorlagendefinition die Deklaration Nr. 1, die nicht mit dem instanziierten Freund von übereinstimmtFoo<int>
. Deklaration Nr. 3 wird nicht einmal berücksichtigt, da eine nicht qualifizierte Suche in der Vorlage sie nicht findet.Mit
-UWRONG_DECL
, GCC (in C ++ 17 und früher) und Clang sind sich einig, dass dieses Programm aus einem anderen Grund schlecht geformt ist: Die unqualifizierte Suche nachoperator==
in Zeile 2 findet nichts.Aber mit
-UWRONG_DECL
scheint GCC im C ++ 20-Modus zu entscheiden, dass es in Ordnung ist, dass die unqualifizierte Sucheoperator==
in # 2 fehlschlägt (vermutlich aufgrund von P0846R0), und scheint dann die Suche aus dem Kontext der Vorlageninstanziierung zu wiederholen, wobei nun # 3 in gefunden wird Verstoß gegen die normale zweiphasige Namenssuchregel für Vorlagen.quelle