2011-01-11 14 views
36

Przeczytałem kilka pytań dotyczących mojego problemu na stackoverflow teraz i żaden z nich nie wydaje się rozwiązać mojego problemu. Albo może zrobiłem to źle ... Przeciążony <<, jeśli zrobię to w funkcji inline. Ale jak sprawić, żeby działało w moim przypadku?przeciążanie operatora przyjaciela << dla szablonu klasy

warning: friend declaration std::ostream& operator<<(std::ostream&, const D<classT>&)' declares a non-template function

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

/tmp/cc6VTWdv.o:uppgift4.cc:(.text+0x180): undefined reference to operator<<(std::basic_ostream<char, std::char_traits<char> >&, D<int> const&)' collect2: ld returned 1 exit status

template <class T> 
T my_max(T a, T b) 
{ 
    if(a > b)  
     return a; 
    else 
     return b; 
} 

template <class classT> 
class D 
{ 
public: 
    D(classT in) 
     : d(in) {}; 
    bool operator>(const D& rhs) const; 
    classT operator=(const D<classT>& rhs); 

    friend ostream& operator<< (ostream & os, const D<classT>& rhs); 
private: 
    classT d; 
}; 


int main() 
{ 

    int i1 = 1; 
    int i2 = 2; 
    D<int> d1(i1); 
    D<int> d2(i2); 

    cout << my_max(d1,d2) << endl; 
    return 0; 
} 

template <class classT> 
ostream& operator<<(ostream &os, const D<classT>& rhs) 
{ 
    os << rhs.d; 
    return os; 
} 
+0

Było to ostatnie pytanie, które może być pouczające: http://stackoverflow.com/questions/4571611/virtual-operator/ –

+0

@Daniel - to nie zajmuje problemu, który mam przy przeciążaniu dla szablonu klasy – starcorn

+5

Myślę, że lepiej jest, jeśli nie zmienisz pytania z podaną odpowiedzią. Utrudnia to ustalenie pierwotnego problemu. Możesz dodać ** EDYTĘ ** na końcu ze zmianą * rozwiązaną * problem, ale uważam, że jest to mylące, gdy pytania zmieniają się w czasie i muszę wyciągnąć historię, aby zobaczyć, co faktycznie było zadawane w pierwsze miejsce. –

Odpowiedz

100

Jest to jedno z najczęściej zadawanych pytań, które mają różne podejścia, które są podobne, ale nie do końca podobne. Te trzy podejścia różnią się tym, o kim deklarujesz, że jesteś przyjacielem swojej funkcji - a następnie o tym, jak ją realizujesz.

ekstrawertyk

zadeklarowania wszystkich dawałaby szablonu jako przyjaciół. To jest to, co zaakceptowaliście jako odpowiedź, a także to, co proponuje większość innych odpowiedzi. W tym podejściu niepotrzebnie otwierasz swoją konkretną instancję D<T>, deklarując znajomym wszystkie instancje operator<<. Oznacza to, że std::ostream& operator<<(std::ostream &, const D<int>&) ma dostęp do wszystkich elementów wewnętrznych D<double>.

template <typename T> 
class Test { 
    template <typename U>  // all instantiations of this template are my friends 
    friend std::ostream& operator<<(std::ostream&, const Test<U>&); 
}; 
template <typename T> 
std::ostream& operator<<(std::ostream& o, const Test<T>&) { 
    // Can access all Test<int>, Test<double>... regardless of what T is 
} 

W introwertycy

wykazać tylko konkretnej instancji operatora wkładania jak listy znajomych. D<int> może lubić operatora wstawiania, gdy zostanie zastosowany do niego samego, ale nie chce mieć nic wspólnego z std::ostream& operator<<(std::ostream&, const D<double>&).

Można to zrobić na dwa sposoby, prosty sposób bycia jako @Emery Berger proponuje, który jest inline operator --which to również dobry pomysł z innych powodów:

template <typename T> 
class Test { 
    friend std::ostream& operator<<(std::ostream& o, const Test& t) { 
     // can access the enclosing Test. If T is int, it cannot access Test<double> 
    } 
}; 

W tej pierwszej wersji , jesteś , a nie tworząc szablonową, operator<<, ale raczej niesformowaną funkcję dla każdego wystąpienia szablonu Test. Znowu różnica jest subtelna, ale jest to zasadniczo równoznaczne z ręcznym dodawaniem: std::ostream& operator<<(std::ostream&, const Test<int>&) podczas tworzenia instancji Test<int> i innego podobnego przeciążenia podczas tworzenia Test z double lub z dowolnego innego typu.

Trzecia wersja jest bardziej uciążliwa. Bez inline kod i przy użyciu szablonu, można zadeklarować jeden konkretyzacji szablonu przyjacielem swojej klasie, nie otwierając sobie wszystkie inne dawałaby:

// Forward declare both templates: 
template <typename T> class Test; 
template <typename T> std::ostream& operator<<(std::ostream&, const Test<T>&); 

// Declare the actual templates: 
template <typename T> 
class Test { 
    friend std::ostream& operator<< <T>(std::ostream&, const Test<T>&); 
}; 
// Implement the operator 
template <typename T> 
std::ostream& operator<<(std::ostream& o, const Test<T>& t) { 
    // Can only access Test<T> for the same T as is instantiating, that is: 
    // if T is int, this template cannot access Test<double>, Test<char> ... 
} 

wykorzystując ekstrawertyk

Subtelna różnica między tą trzecią opcją a pierwszą polega na tym, ile otwierasz na inne klasy. Przykładem nadużycia w ekstrawertyk wersji byłby ktoś, który chce uzyskać dostęp do swoich wewnętrznych i robi to:

namespace hacker { 
    struct unique {}; // Create a new unique type to avoid breaking ODR 
    template <> 
    std::ostream& operator<< <unique>(std::ostream&, const Test<unique>&) 
    { 
     // if Test<T> is an extrovert, I can access and modify *any* Test<T>!!! 
     // if Test<T> is an introvert, then I can only mess up with Test<unique> 
     // which is just not so much fun... 
    } 
} 
+5

o wiele jaśniejsza niż wybrana odpowiedź – TheInvisibleFist

+0

Implementacja funkcji poza klasą daje niezdefiniowane odniesienie. Wewnątrz klasy dostaję problem, gdy tylko jednoznacznie zainicjuję klasę hosta więcej niż jednym typem. – dgrat

+0

@dgrat: Nie mogę debugować kodu, którego nie widzę, ale działa. Czy znajomy jest zależny od któregokolwiek z argumentów szablonu? Gdyby tak nie było, pojawiłyby się problemy z wieloma definicjami, ponieważ różne wystąpienia generowałyby dokładnie tę samą funkcję (lub naruszenie ODR, jeśli podpis funkcji jest taki sam, ale ciało różni się). –

11

Nie można zadeklarować przyjaciela takiego, trzeba określić inny typ szablonu dla niego.

template <typename SclassT> 
friend ostream& operator<< (ostream & os, const D<SclassT>& rhs); 

uwaga SclassT tak, że nie ma cienia classT. Podczas definiowania

+0

dziękuję tym pracom zredagowałem moje pytanie z tym kodem, zaznaczę to jako odpowiedź, gdy tylko pasek zniknie. – starcorn

+4

Należy zauważyć, że nie deklaruje to konkretnej instancji 'operatora <<' jako przyjaciela, ale raczej ** wszystkie ** wystąpienia, w tym dowolną specjalizację szablonu. Zobacz odpowiedź [tutaj] (http://stackoverflow.com/questions/4660123/overloading-friend-operator-for-template-class/4661372#4661372) –

+0

@starcorn powinieneś zmienić wybraną odpowiedź, aby zapewnić lepszą odpowiedź a to powinna być odpowiedź Davida Rodrigueza. –

2

To działało dla mnie bez ostrzeżenia kompilatora.

#include <iostream> 
using namespace std; 

template <class T> 
T my_max(T a, T b) 
{ 
    if(a > b) 
    return a; 
    else 
    return b; 
} 

template <class classT> 
class D 
{ 
public: 
    D(classT in) 
    : d(in) {}; 

    bool operator>(const D& rhs) const { 
    return (d > rhs.d); 
    } 

    classT operator=(const D<classT>& rhs); 

    friend ostream& operator<< (ostream & os, const D& rhs) { 
    os << rhs.d; 
    return os; 
    } 

private: 
    classT d; 
}; 


int main() 
{ 

    int i1 = 1; 
    int i2 = 2; 
    D<int> d1(i1); 
    D<int> d2(i2); 

    cout << my_max(d1,d2) << endl; 
    return 0; 
} 
+0

Tak, już to zrobiłem, ale co o jeśli nie chcę, aby 'operator <<' był funkcją inline? – starcorn

+0

@starcorn: Wheather Metoda/funkcja jest taged w linii (niejawnie lub jawnie) ma mało do zrobienia pogoda funkcja jest rzeczywiście inline w kodzie. Dlatego jest to bezsensowne zmartwienie. –

+0

+1. @starcorn: To rozwiązanie jest lepsze niż zaakceptowane. Różnica jest subtelna, ale w zaakceptowanej odpowiedzi deklarujesz wszystkie wystąpienia (i możliwe specjalizacje) operatora '' jako przyjaciele, podczas gdy w tym rozwiązaniu dajesz tylko dostęp do instancji 'operatora <<' ma ten sam typ. Ponadto, jako efekt uboczny definiowania 'operatora <<' wewnątrz klasy ograniczasz widoczność tego 'operatora <<' tylko do tych przypadków, w których jeden z dwóch argumentów jest 'D' - kompilator nie będzie nawet rozważ przeciążenie 'operatora <<', chyba że jeden argument to 'D '. –

0

Proszę bardzo:

#include <cstdlib> 
#include <iostream> 
using namespace std; 

template <class T> 
T my_max(T a, T b) 
{ 
    if(a > b)  
     return a; 
    else 
     return b; 
} 

template <class classT> 
class D 
{ 
public: 
    D(classT in) 
     : d(in) {}; 
    bool operator>(const D& rhs) const { return d > rhs.d;}; 
    classT operator=(const D<classT>& rhs); 

    template<class classT> friend ostream& operator<< (ostream & os, const D<classT>& rhs); 
private: 
    classT d; 
}; 

template<class classT> ostream& operator<<(ostream& os, class D<typename classT> const& rhs) 
{ 
    os << rhs.d; 
    return os; 
} 


int main() 
{ 

    int i1 = 1; 
    int i2 = 2; 
    D<int> d1(i1); 
    D<int> d2(i2); 

    cout << my_max(d1,d2) << endl; 
    return 0; 
} 
+0

Nie sądzę, że to powinno się skompilować: 'template ostream & operator << (ostream i os, class D const i rhs)'. Opracowany typ nie jest dozwolony w deklaracji parametrów, a "typename" wymaga kwalifikowanego identyfikatora. –

+0

@Gene: hmm. Kompiluje się dla mnie na najwyższych poziomach z wyłączonymi rozszerzeniami MS. –

+0

Nie kompiluje się z g ++, a ja ufam kompilatorowi w tym. Drugi argument w 'operator <<' to 'class D ' i myślę, że jest niepoprawny. Zamiast tego użyłbym 'D '. Słowo kluczowe "klasa" jest tam opcjonalne (99,9% przypadków), ale użycie "typename" nie jest jednym z dwóch znanych zastosowań: byłoby nieważne jako substytut "klasy" i polega na zidentyfikowaniu tego nazwa zależna od szablonu jest w rzeczywistości typem. –

0

myślę, że nie powinno się znajomego w pierwszej kolejności.

Można utworzyć publiczną metodę połączenia drukować coś takiego (w przypadku braku klasy szablonu):

std::ostream& MyClass::print(std::ostream& os) const 
{ 
    os << "Private One" << privateOne_ << endl; 
    os << "Private Two" << privateTwo_ << endl; 
    os.flush(); 
    return os; 
} 

a następnie, poza klasą (ale w tej samej przestrzeni nazw)

std::ostream& operator<<(std::ostream& os, const MyClass& myClass) 
{ 
    return myClass.print(os); 
} 

Myślę, że powinien działać również dla klasy szablonów, ale jeszcze nie testowałem.

Powiązane problemy