2013-04-22 4 views
6

Perché non è possibile utilizzare lo stesso parametro modello per una funzione amico che accetta un argomento modello? Voglio dire che il codice qui sotto è OK!<< (ostream & os, ...) per modello classe

template <class Vertex> 
class Edge 
{ 
    template <class T> 
    friend ostream& operator<<(ostream& os, const Edge<T>& e); 
    /// ... 
}; 


template <class T> 
ostream& operator<<(ostream& os, const Edge<T>& e) 
{ 
    return os << e.getVertex1() << " -> " << e.getVertex2(); 
} 

Ma questo NON è ok. Perché? Qual è il problema? (Ottengo l'errore del linker.)

template <class Vertex> 
class Edge 
{ 
    friend ostream& operator<<(ostream& os, const Edge<Vertex>& e); 
    /// ... 
}; 

template <class T> 
ostream& operator<<(ostream& os, const Edge<T>& e) 
{ 
    return os << e.getVertex1() << " -> " << e.getVertex2(); 
} 
+1

Un amico è un modello, l'altro è non. – Xeo

risposta

3

È possibile utilizzare seguente

template <class Vertex> 
class Edge 
{ 
    friend ostream& operator<< <>(ostream& os, const Edge<Vertex>& e); 
    /// ... 
}; 

che rende operator << <Vertex> amico Edge.

Nel suo secondo caso - si effettua amico operatore non modello, ma la definizione di questo operatore è modello, in modo da avere undefined riferimento, ma questo caso può essere utilizzato se si desidera che il operator << per calcestruzzo Edge (Edge<int> per esempio) .

+1

Ciò richiederà una dichiarazione in avanti del modello di funzione 'operator <<' (che a sua volta richiederà una dichiarazione in avanti del modello di classe 'Edge'). –

2
template <class T> 
friend ostream& operator<<(ostream& os, const Edge<T>& e); 

Says, c'è su modellioperator << fuori, essere amico con esso, e tutto è OK.

 

friend ostream& operator<<(ostream& os, const Edge<Vertex>& e); 

Dice, c'è un operator << di fuori, essere amico con esso ... e compilatore non riesce a trovare una cosa del genere.

Per dire al compilatore che l'operatore è un modello, aiutarlo con un <> come menzionato prima (lui mi ha battuto rapidamente MrGreen).

2

Il problema è che qui:

friend ostream& operator<<(ostream& os, const Edge<Vertex>& e); 

si dichiara una funzione non template (che sarà diverso per ogni istanza di Edge) per essere un friend, e non l'istanza del modello .

La soluzione più comune qui che ho visto è stata semplicemente la operator<< in linea nella definizione di modello di classe . In alternativa, è possibile fornire una funzione di membro pubblico che esegue l'output e richiamarla dal modello di funzione operator<<. Oppure si può scrivere:

friend ostream& operator<< <Vertex>(ostream&, Edge<Vertex> const&); 

per dire al compilatore che la operator<< che è un amico è un'istanza di una mascherina. IIUC, tuttavia, funziona solo se esiste una dichiarazione del modello della funzione visibile a questo punto, il che significa che è necessario inoltrarlo a (e inoltrarlo dichiararlo a avanti dichiarare la classe modello).

La mia soluzione al solito per questo tipo di problema è quello di fornire un normale funzione membro print, e quindi deriva da:

template <typename DerivedType> 
class IOStreamOperators 
{ 
public: 
    friend std::ostream&operator<<(
     std::ostream&  dest, 
     DerivedType const& source) 
    { 
     source.print(dest) ; 
     return dest ; 
    } 

    friend std::istream&operator>>(
     std::istream&  source, 
     DerivedType&  dest) 
    { 
     dest.scan(source) ; 
     return source ; 
    } 

protected: 
    ~IOStreamOperators() {} 
}; 

, ad esempio:

template <class Vertex> 
class Edge : public IOStreamOperators<Edge<Vertex> > 
{ 
    // ... 
    void print(std::ostream& dest) 
    { 
     // ... 
    } 
}; 

ho trovato che questo generalmente rende il codice più semplice e più semplice da seguire alla fine.

1

penso che sia più facile da capire se togliamo il rumori estranei e consideriamo:

template <typename T> 
struct X 
{ 
    friend void f(X<T>& x) { } 
}; 

template <typename T> 
void f(const X<T>& x) { } 
  • Il f all'interno X è: void f(X<T>& x)
  • Il f esterno X è: void f<T>(X<T>& x)

Puoi farti un'idea compilando e guardando t egli simboli generati:

00410aa8 t .text$_Z1fR1XIdE 
00410ad4 t .text$_Z1fIdEvRK1XIT_E 

Chiamata del GCC __PRETTY_FUNCTION__ da ciascuno dei rendimenti:

void f(X<double>&) 
void f(const X<T>&) [with T = double] 

Non particolarmente chiaro, ma modo di dire del GCC del void f<double>(...) quest'ultimo.

Personalmente, per i modelli tendo a definire la funzione della classe ... non è necessario menzionare l'aspetto del modello a tutti, solo:

friend ostream& operator<<(ostream& os, const Edge& e) 
{ 
    // use e.whatever... 
}