2010-10-21 26 views
8

Cosa c'è di sbagliato nel mio codice?C++ template friend operator overloading

template<int E, int F> 
class Float 
{ 
friend Float<E, F> operator+ (const Float<E, F> &lhs, const Float<E, F> &rhs); 
}; 

G ++ mantiene solo avvertimento:

float.h:7: warning: friend declaration ‘Float<E, F> operator+(const Float<E, F>&, const Float<E, F>&)’ declares a non-template function

float.h:7: warning: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) -Wno-non-template-friend disables this warning

ho cercato di add <> after the function name here come accennato nella nota di avvertimento, ma g ++ mi dà un errore.

Ho compilato il codice con clang ++, è andato tutto bene, nessun avviso.

+3

Si consiglia di leggere http://www.parashift.com/c++-faq-lite/templates.html#faq-35.16 – UncleBens

risposta

24

È solo un avvertimento su un aspetto difficile della lingua. Quando si dichiara una funzione friend, non è un membro della classe in cui si trova la dichiarazione. È possibile definirlo lì per comodità, ma in realtà appartiene allo spazio dei nomi.

Dichiarare una funzione amico che non è un modello, all'interno di un modello di classe, dichiara ancora una funzione non modello nello spazio dei nomi. Non è né un membro della classe, né esso stesso un modello. Tuttavia, è il generato dal modello di classe.

La generazione di funzioni non modello da un modello è un po 'confusa. Ad esempio, non è possibile aggiungere una dichiarazione per tale funzione al di fuori del blocco class. Pertanto è necessario definirlo anche all'interno del blocco class, il che ha senso perché il modello di classe lo genererà.

Un'altra cosa complicata per gli amici è che la dichiarazione all'interno di class Float {} non dichiara la funzione nello spazio dei nomi. Lo si può trovare solo attraverso la risoluzione di overload del significato dipendente dall'argomento, ad esempio specificando che un argomento ha tipo Float (o un riferimento o un puntatore). Questo non è un problema per operator+, poiché è probabile che sia sovraccaricato in ogni caso e non verrà mai chiamato, tranne che per i tipi definiti dall'utente.

Per un esempio di potenziale problema, immagina di avere un costruttore di conversioni Float::Float(Bignum const&). Ma Bignum non ha operator+. (Spiacente, esempio forzato.) Si desidera fare affidamento su operator+(Float const&, Float const&) per l'aggiunta Bignum. Ora my_bignum + 3 non verrà compilato perché nessuno degli operandi è un Float quindi non riesce a trovare la funzione friend.

Probabilmente, non hai nulla di cui preoccuparti, purché la funzione in questione sia operator.

Oppure, è possibile modificare lo friend come modello. In tal caso, deve essere definito all'esterno del del blocco class {} e dichiarato prima, invece di dover essere dichiarato e definito all'interno di.

template<int E, int F> // now this is a template! 
Float<E, F> operator+ (const Float<E, F> &lhs, const Float<E, F> &rhs); 

template<int E, int F> 
class Float 
{ 
    // deduce arguments E and F - this names operator+< E, F >. 
friend Float<E, F> operator+<> (const Float<E, F> &lhs, const Float<E, F> &rhs); 
}; 
+1

Grazie per la tua gentile risposta. –

1

Hai bisogno di fare esattamente come dicono gli avvertimenti:

template<int E, int F> 
Float<E, F> operator+ (const Float<E, F> &lhs, const Float<E, F> &rhs); 

template<int E, int F> 
class Float 
{ 
friend Float<E, F> operator+<> (const Float<E, F> &lhs, const Float<E, F> &rhs); 
}; 

Questo dichiara una specializzazione completa del modello di operatore di un amico di una specifica istanza del modello di classe. In un commento alla domanda, UncleBens ha gentilmente fornito a link to an explanation perché è così complicato.

+0

@Potatoswatter: La dichiarazione amico dichiara una specializzazione specifica un amico della classe, no? – sbi

+0

In realtà, ho appena testato il codice @ sbi e funziona con il mio g ++. –

+0

@Zifei: questo frammento funziona, ma richiede la modifica della definizione della funzione. (Scusa, non ho notato la modifica al modulo della funzione.) – Potatoswatter

3

Questo è un argomento piuttosto vecchio, ma penso che il modo più semplice per dichiarare l'operatore sia definirlo all'interno della classe Float.

template<int E, int F> 
class Float 
{ 
public: 
    friend Float operator+ (const Float &lhs, const Float &rhs) 
    { 
     // Whatever you need to do. 
    } 
}; 

La sintassi è più facile scrivere e capire e funziona esattamente lo stesso (tranne che sarà inline), non sarà una funzione membro.

MSDN: Le funzioni di amico definite all'interno delle dichiarazioni di classe non sono considerate nell'ambito della classe di chiusura; sono nello scope del file.

+0

No, nel caso di 'operator +', generalmente il modo più semplice e migliore è implementare 'operator + =' come membro, e quindi implementare 'operator +' come non-membro non-membro in termini di '+ = '. Ciò garantisce che entrambi gli operatori siano definiti e si comportino in modo coerente. –