2013-04-22 12 views
6

Dlaczego nie mogę użyć tego samego parametru szablonu dla funkcji znajomego, która przyjmuje argument szablonu? Mam na myśli poniższy kod jest OK!operator << (ostream & os, ...) dla klasy szablonów

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

Ale ten NIE jest w porządku. Czemu? Jaki jest problem? (Otrzymuję błąd linkera.)

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

Jeden przyjaciel jest szablon, drugi jest nie. – Xeo

Odpowiedz

3

można użyć następujących

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

sprawia, że ​​operator << <Vertex> przyjaciel Edge.

W swoim drugim przypadku - dokonać przyjaciela operatora bez szablonu, ale definicja tego operatora jest szablon, więc masz niezdefiniowane odniesienia, ale w tym przypadku może być używany, jeśli chcesz operator << dla betonu Edge (Edge<int> na przykład) .

+1

Wymagać to będzie przekazania do przodu szablonu funkcji 'operator <<' (który z kolei będzie wymagał przekazania deklaracji szablonu klasy "Edge"). –

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

Mówi istnieje matrycyoperator << zewnątrz, być przyjaciel z nim, i wszystko jest OK.

 

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

Mówi, istnieje operator << zewnątrz, być przyjaciel z nim ... i kompilator nie może znaleźć coś takiego.

Aby powiedzieć kompilatorowi, że operator jest szablonem, pomóż mu przez <> jako wspomniany ForEveR (pobił mnie szybko :-D).

2

Problemem jest to, że tutaj:

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

Jesteś deklarowania funkcji non-szablonu (który będzie różne dla każdej instancji z Edge) być friend, a nie instancji szablonu .

Najbardziej rozpowszechnionym rozwiązaniem, które widziałem było po prostu zaimplementować operator<< w linii, w definicji szablonu klasy . Alternatywnie można udostępnić publiczną funkcję, która wykonuje dane wyjściowe i wywołać ją z szablonu funkcji operator<<. Można też napisać:

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

aby poinformować kompilator, że operator<< który jest przyjacielem jest instancji szablonu. IIUC działa jednak tylko , jeśli w tym miejscu widoczna jest deklaracja docelowego szablonu operator<< , co oznacza, że ​​musisz forward zadeklarować (i w celu przekazania go zadeklarować, do dalej zadeklarować klasę szablon).

My zwykle rozwiązaniem tego typu problemów jest zapewnienie normalną funkcję składową print, a potem czerpać z:

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() {} 
}; 

, np:

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

Odkryłam, że to ogólnie czyni kod prostszym i łatwiejszym do naśladowania w końcu.

1

myślę, że najłatwiej zrozumieć, jeśli usuwamy szum obcych i rozważyć:

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

template <typename T> 
void f(const X<T>& x) { } 
  • f wewnątrz X jest: void f(X<T>& x)
  • f poza X jest: void f<T>(X<T>& x)

Możesz to uzyskać, kompilując i patrząc na t he symbole generowane:

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

Wywołanie GCC __PRETTY_FUNCTION__ od siebie wydajnością:

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

Nie jest szczególnie wyraźny, ale sposób GCC na powiedzenie tego ostatniego void f<double>(...).

Osobiście szablonów Mam tendencję do określenia funkcji w klasie ... nie trzeba wspomnieć aspekt szablonów w ogóle, po prostu:

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