Una piccola panoramica. Sto scrivendo un modello di classe che fornisce un typedef forte; da forte typedef sto contrastando con un typedef normale che dichiara semplicemente un alias. Per dare un'idea:Perché gli operatori di confronto std :: vector e std :: string sono definiti come funzioni modello?
using EmployeeId = StrongTypedef<int>;
Ora, ci sono diverse scuole di pensiero su forti typedef e conversioni implicite. Una di queste scuole dice: non tutti i numeri interi sono EmployeeId, ma ogni EmployeeId è un numero intero, quindi è necessario consentire conversioni implicite da EmployeeId a interi. E si può implementare questa, e scrivere le cose come:
EmployeeId x(4);
assert(x == 4);
Questo funziona perché x
ottiene implicitamente convertito in un intero, e quindi viene utilizzato il confronto intero uguaglianza. Fin qui tutto bene. Ora, voglio fare questo con un vettore di interi:
using EmployeeScores = StrongTypedef<std::vector<int>>;
modo che io possa fare cose come questa:
std::vector<int> v1{1,2};
EmployeeScores e(v1);
std::vector<int> v2(e); // implicit conversion
assert(v1 == v2);
Ma non riesco ancora a fare questo:
assert(v1 == e);
Il motivo per cui questo non funziona dipende da come std::vector
definisce il controllo di uguaglianza, fondamentalmente (modulo standardese):
template <class T, class A>
bool operator==(const vector<T,A> & v1, const vector<T,A> & v2) {
...
}
Questo è un modello di funzione; poiché viene scartato in una fase precedente di ricerca, non consentirà un tipo che converte implicitamente in vettoriale per essere confrontato.
Un modo diverso per definire parità sarebbe come questo:
template <class T, class A = std::allocator<T>>
class vector {
... // body
friend bool operator==(const vector & v1, const vector & v2) {
...
}
} // end of class vector
In questo secondo caso, l'operatore di uguaglianza non è un modello di funzione, è solo una funzione regolare che è generato insieme alla classe, simile a una funzione membro. Questo è un caso insolito abilitato dalla parola chiave friend.
La domanda (mi dispiace lo sfondo è stato così lungo), è perché non std::vector
utilizzare il secondo modulo invece del primo? Questo rende vector
più simile ai tipi primitivi e, come puoi vedere chiaramente, aiuta nel mio caso d'uso. Questo comportamento è ancora più sorprendente con string
poiché è facile dimenticare che string
è solo un typedef di un modello di classe.
Due cose che ho considerato: in primo luogo, alcuni potrebbero pensare che poiché la funzione friend viene generata con la classe, ciò causerà un errore grave se il tipo contenuto del vettore non supporta il confronto di uguaglianza. Questo non è il caso; come le funzioni membro delle classi template, non vengono generate se non utilizzate. In secondo luogo, nel caso più generale, le funzioni libere hanno il vantaggio che non è necessario che siano definite nella stessa intestazione della classe, che può avere vantaggi. Ma questo chiaramente non è utilizzato qui.
Quindi, cosa dà? C'è una buona ragione per questo, o era solo una scelta sub-ottimale?
Edit: ho scritto un esempio veloce che dimostra due cose: sia che la conversione implicita funziona, se lo desideri con l'approccio amico, e che non gli errori sono causati duri se il tipo su modelli non soddisfa i requisiti del operatore di uguaglianza (ovviamente, assumendo che l'operatore di uguaglianza non sia usato in quel caso).Modifica: migliorato per contrastare con il primo approccio: http://coliru.stacked-crooked.com/a/6f8910945f4ed346.
perché è stato downvoted? – Untitled123
Sei sicuro che con 'friend'version,' assert (v1 == e); 'compilerebbe? – YSC
@ YSC Sì, ho aggiunto un collegamento a Coliru, fammi sapere se risponde alla tua domanda completamente. –