Ci sono due questioni irrisolte riguardanti modelli friend
funzione definita in modelli di classe: 1545 e 2174. Il primo mette in discussione la misura in cui è valido e il secondo riguarda le violazioni odr che possono sorgere in base alle istanze effettive di quei modelli di funzione. Non sono sicuro di quale compilatore sia corretto (avendo precedentemente creduto che entrambi fossero sbagliati), ma potrebbe semplicemente essere sottovalutato o scarsamente specificato nello standard quale sia il comportamento corretto in questa situazione.
Il codice dovrebbe idealmente compilare (in attesa di risoluzione dei problemi):
template<typename ...Args>
class obj {
bool p = false;
template<typename T, typename... Args2>
friend T get(const obj<Args2...> &o) { return o.p; }
};
template<typename T, typename... Args>
T get(const obj<Args...> &o);
La dichiarazione friend
prima dichiara get
, quindi questo crea un nuovo membro dello spazio dei nomi più interno: ::get
. La dichiarazione esterna semplicemente ridisegna la stessa funzione, quindi c'è davvero solo quella ::get
. Da [temp.over.link]:
Due espressioni che coinvolgono parametri del modello sono considerate equivalenti se due definizioni di funzioni che contengono le espressioni avrebbe soddisfatto la regola una definizione (3.2), tranne che i token utilizzati per il nome del I parametri del modello possono variare a condizione che un token utilizzato per denominare un parametro modello in un'espressione sia sostituito da un altro token che denomina lo stesso parametro del modello nell'altra espressione. Per determinare se due nomi dipendenti (14.6.2) sono equivalenti, viene considerato solo il nome stesso, non il risultato della ricerca del nome nel contesto del modello.
Usando diversi nomi dei parametri del modello (Args...
vs Args2...
) va bene - questa seconda dichiarazione del modello di funzione ::get
è valido e permette per la ricerca per trovarlo.
Questo ci porta a:
bool test(const obj<int, float, double> &a) {
#ifdef UNQUAL
return get<int>(a); // unqualified
#else
return ::get<int>(a); // qualified
#endif
}
Entrambi questi dovrebbe lavoro - da quando abbiamo dichiarato nuovamente la funzione di essere in ambito spazio dei nomi, non hanno nemmeno bisogno di preoccuparsi di ADL.Entrambe le chiamate dovrebbero trovare ::get<int>(obj<int, float, double>)
, che è un friend
, quindi il codice dovrebbe essere compilato e collegato. gcc consente la chiamata non qualificata ma non la chiamata qualificata, clang non consente né.
Potremmo eludere entrambe le questioni CWG interamente semplicemente non definizione del modello di funzione all'interno della classe:
template<typename ...Args>
class obj {
bool p = false;
template<typename T, typename... Args2>
friend T get(const obj<Args2...> &o);
};
template<typename T, typename... Args>
T get(const obj<Args...> &o) { return o.p; }
Con questa formulazione, entrambe le compilazioni permettono sia invocazione qualificati e non qualificati di get
.
Se riscrivere il codice in modo tale che obj
è una classe e non un modello di classe, ma tutto il resto è uguale:
class obj {
bool p = false;
template <class T>
friend T get(const obj& o) { return o.p; }
};
template <class T> T get(const obj&);
bool test(const obj& a) {
#ifdef UNQUAL
return get<int>(a);
#else
return ::get<int>(a);
#endif
}
Entrambi i compilatori consentono entrambe le invocazioni. Ma non c'è alcuna differenza di cui io sia a conoscenza nelle regole tra obj
come una normale classe e un modello di classe.
Perché dichiarando 'template T get (const obj &o); classe' fuori –
Jarod42
Perché non funziona se non si ... provare per credere:? Https://godbolt.org/g/peSscU –
Sì, 'T' non è deducibile, quindi devi fornire' ', quindi ADL non funziona (dato che non ci sono template' get' in ambito globale):/Puoi dichiarare un modello fittizio ottenere (con corrispondenza non corretta) per riattivare ADL: [Demo] (http://coliru.stacked-crooked.com/a/e40aaf90de712943). –
Jarod42