Definizione fuori classe C ++ 20 in una classe modello


12

Fino allo standard C ++ 20 di C ++, quando volevamo definire un operatore fuori classe che utilizza alcuni membri privati ​​di una classe template, useremmo un costrutto simile a questo:

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

Dal C ++ 20, tuttavia, possiamo omettere la dichiarazione fuori classe, quindi anche la dichiarazione in avanti, in modo da poter fare a meno di:

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

Ora, la mia domanda è: quale parte di C ++ 20 ci consente di farlo? E perché ciò non era possibile nei precedenti standard C ++?


Come è stato sottolineato nei commenti, clang non accetta questo codice presentato nella demo, il che suggerisce che questo potrebbe effettivamente essere un bug in gcc.

Ho presentato una segnalazione di bug sul bugzilla di gcc


2
Personalmente preferisco la definizione della classe, evitando la funzione template (e deducendo "problemi" (nessuna corrispondenza per "c string" == Foo<std::string>("foo"))).
Jarod42,

@ Jarod42 Sono pienamente d'accordo, preferisco anche la definizione in classe. Sono stato solo sorpreso di scoprire che C ++ 20 ci consente di non ripetere la firma della funzione tre volte quando la si definisce ouf-of-class, che può essere utile in un'API pubblica in cui l'implementazione è in un file .inl nascosto.
ProXicT

Non ho notato che era impossibile. Come mai l'ho usato finora senza problemi?
ALX23z,

1
Hmmm, in temp.friend , non è cambiato molto, soprattutto non 1.3 che dovrebbe essere responsabile di questo comportamento. Dal momento che clang non accetta il tuo codice, sono propenso a che gcc abbia un bug.
n314159,

@ ALX23z Funziona senza la dichiarazione fuori classe se la classe non è basata su modelli.
ProXicT il

Risposte:


2

GCC ha un bug.

La ricerca del nome viene sempre eseguita per i nomi dei modelli che compaiono prima di un <, anche quando il nome in questione è il nome dichiarato in una dichiarazione (amico, specializzazione esplicita o istanza esplicita).

Poiché il nome operator==nella dichiarazione di amicizia è un nome non qualificato ed è soggetto alla ricerca del nome in un modello, si applicano le regole di ricerca del nome in due fasi. In questo contesto, operator==non è un nome dipendente (non fa parte di una chiamata di funzione, quindi ADL non si applica), quindi il nome viene cercato e associato nel punto in cui appare (vedere [temp.nondep] paragrafo 1). Il tuo esempio non è corretto perché la ricerca di questo nome non trova alcuna dichiarazione operator==.

Mi aspetto che GCC lo accetti in modalità C ++ 20 a causa di P0846R0 , che consente (ad esempio) operator==<T>(a, b)di essere utilizzato in un modello anche se non operator==è visibile alcuna dichiarazione precedente come modello.

Ecco una prova ancora più interessante:

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;

Con -DWRONG_DECL, GCC e Clang concordano sul fatto che questo programma è mal formato: la ricerca non qualificata per la dichiarazione di amico n. 2, nel contesto della definizione del modello, trova la dichiarazione n. 1, che non corrisponde all'amico istanziato di Foo<int>. La dichiarazione n. 3 non viene nemmeno presa in considerazione, poiché la ricerca non qualificata nel modello non la trova.

Con -UWRONG_DECL, GCC (in C ++ 17 e precedenti) e Clang concordano sul fatto che questo programma è mal formato per una ragione diversa: la ricerca non qualificata per la operator==linea # 2 non trova nulla.

Ma con -UWRONG_DECL, GCC in modalità C ++ 20 sembra decidere che è OK che la ricerca non qualificata operator==in # 2 fallisce (presumibilmente a causa di P0846R0), e quindi sembra rifare la ricerca dal contesto di istanza del modello, trovando ora # 3, in violazione della normale regola di ricerca del nome in due fasi per i modelli.


Grazie per questa spiegazione dettagliata, molto ben messo!
ProXicT
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.