C ++ 20-Definition außerhalb der Klasse in einer Vorlagenklasse

12

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;
}

Demo

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

ProXicT
quelle
2
Ich persönlich bevorzuge bei der Klassendefinition die Vermeidung von Vorlagenfunktionen (und den Abzug von "Problemen" (keine Übereinstimmung für "c string" == Foo<std::string>("foo"))).
Jarod42
@ Jarod42 Ich stimme vollkommen zu, ich bevorzuge auch die Definition in der Klasse. Ich war nur überrascht, als ich herausfand, dass C ++ 20 es uns ermöglicht, die Funktionssignatur nicht dreimal zu wiederholen, wenn wir sie als Ouf-of-Class definieren. Dies kann in einer öffentlichen API nützlich sein, in der sich die Implementierung in einer versteckten INL-Datei befindet.
ProXicT
Ich habe nicht bemerkt, dass es unmöglich war. Wie kommt es, dass ich es bisher ohne Probleme verwendet habe?
ALX23z
1
Hmmm, in temp.friend hat sich nicht viel geändert, insbesondere nicht 1.3, was für dieses Verhalten verantwortlich sein sollte. Da clang Ihren Code nicht akzeptiert, neige ich dazu, dass gcc einen Fehler hat.
n314159
@ ALX23z Es funktioniert ohne die Deklaration außerhalb der Klasse, wenn die Klasse keine Vorlage enthält.
ProXicT

Antworten:

2

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 Zusammenhang operator==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 findet operator==.

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 Deklaration operator==als Vorlage sichtbar ist.

Hier ist ein noch interessanterer Testfall:

template <typename T> struct Foo;

#ifdef WRONG_DECL
template <typename T> bool operator==(Foo<T> lhs, int); // #1
#endif

template <typename T> struct Foo {
  friend bool operator==<T>(Foo<T> lhs, float); // #2
};

template <typename T> bool operator==(Foo<T> lhs, float); // #3
Foo<int> f;

Mit -DWRONG_DECLstimmen 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 übereinstimmt Foo<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 nach operator==in Zeile 2 findet nichts.

Aber mit -UWRONG_DECLscheint GCC im C ++ 20-Modus zu entscheiden, dass es in Ordnung ist, dass die unqualifizierte Suche operator==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.

Richard Smith
quelle
Vielen Dank für diese ausführliche Erklärung, sehr gut ausgedrückt!
ProXicT