2009-01-24 9 views
171

Piszę małą bibliotekę macierzy w C++ dla operacji macierzowych. Jednak mój kompilator narzeka, gdzie wcześniej nie. Kod ten pozostawiono na półce przez 6 miesięcy, aw międzyczasie uaktualniłem swój komputer z debian etch do lenny (g ++ (Debian 4.3.2-1.1) 4.3.2 ) jednak mam taki sam problem w systemie Ubuntu z tym samym g ++.Jak poprawnie przeciążyć operator << dla ostream?

Oto odpowiednia część mojej klasy matrycy:

namespace Math 
{ 
    class Matrix 
    { 
    public: 

     [...] 

     friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix); 
    } 
} 

I "realizacja":

using namespace Math; 

std::ostream& Matrix::operator <<(std::ostream& stream, const Matrix& matrix) { 

    [...] 

} 

Jest to błąd podane przez kompilator:

matrix.cpp:459: error: 'std::ostream& Math::Matrix::operator<<(std::ostream&, const Math::Matrix&)' must take exactly one argument

Jestem nieco zdezorientowany tym błędem, ale znowu moje C++ stało się trochę zardzewiałe po wykonaniu wielu Javy tych 6 mon ths. :-)

Odpowiedz

99

Użytkownik zadeklarował swoją funkcję jako friend. To nie jest członek klasy. Powinieneś usunąć Matrix:: z wdrożenia. friend oznacza, że ​​określona funkcja (która nie jest członkiem klasy) może uzyskać dostęp do prywatnych zmiennych członków. Sposób, w jaki zaimplementowano tę funkcję, jest podobny do metody instancji klasy Matrix, która jest niepoprawna.

+6

Powinieneś również zadeklarować to w przestrzeni nazw Math (nie tylko przy użyciu Math w przestrzeni nazw). –

+1

Dlaczego 'operator <<' musi znajdować się w przestrzeni nazw 'Math'? Wygląda na to, że powinien znajdować się w globalnej przestrzeni nazw. Zgadzam się, że mój kompilator chce, aby znajdował się w przestrzeni nazw 'Math', ale to nie ma dla mnie sensu. –

58

Aby dodać do Mehrdad odpowiedź

namespace Math 
{ 
    class Matrix 
    { 
     public: 

     [...] 


    } 
    std::ostream& operator<< (std::ostream& stream, const Math::Matrix& matrix); 
} 

W implementacji

std::ostream& operator<<(std::ostream& stream, 
        const Math::Matrix& matrix) { 
    matrix.print(stream); //assuming you define print for matrix 
    return stream; 
} 
+3

Nie rozumiem, dlaczego to jest głosowanie w dół, to wyjaśnia, że ​​możesz zadeklarować, że operator znajduje się w obszarze nazw, a nie jako przyjaciel i jak możesz złożyć deklarację operatorowi. – kal

+2

Odpowiedź Mehrdada nie zawierała żadnego fragmentu kodu, więc właśnie dodałem, co może działać, przenosząc go poza klasę w samej przestrzeni nazw. – kal

+0

Rozumiem twój punkt widzenia, patrzyłem tylko na twój drugi fragment. Ale teraz widzę, że wyjąłeś operatora z klasy. Dzieki za sugestie. –

114

Wystarczy ci opowiadałem jedna możliwość: Lubię stosując definicje przyjaciela za to:

namespace Math 
{ 
    class Matrix 
    { 
    public: 

     [...] 

     friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix) { 
      [...] 
     } 
    }; 
} 

Funkcja zostanie automatycznie skierowana do otaczającej przestrzeni nazw Math (e choć jego definicja pojawia się w zakresie tej klasy), ale nie będzie widoczna, chyba że wywołasz operator o nazwie Matrix, który sprawi, że wyszukiwanie zależne od argumentu znajdzie tę definicję operatora. To może czasem pomóc w niejednoznacznych wywołaniach, ponieważ jest niewidoczne dla typów argumentów innych niż Matrix. Podczas pisania definicji można również bezpośrednio odwoływać się do nazw zdefiniowanych w macierzy i samej macierzy, nie kwalifikując nazwy z możliwie długim prefiksem i podając parametry szablonu, takie jak Math::Matrix<TypeA, N>.

+0

Dziękuję za tę odpowiedź, bardzo mi się to przydało. – tommyk

+4

Myślę, że ta odpowiedź jest lepsza niż zaakceptowana. Dzięki! – PolyMesh

48

Zakładając, że mówimy o przeciążeniu operator << dla wszystkich klas pochodnych od std::ostream obsługiwać klasę Matrix (a nie przeciążenia << dla Matrix klasie), to ma większy sens, aby zadeklarować funkcję przeciążeniem poza nazw Math w nagłówku .

Użyj funkcji znajomego tylko wtedy, gdy nie można uzyskać tej funkcjonalności za pomocą publicznych interfejsów.

Matrix.h

namespace Math { 
    class Matrix { 
     //... 
    }; 
} 
std::ostream& operator<<(std::ostream&, const Math::Matrix&); 

Należy pamiętać, że przeciążenie operator jest zadeklarowana poza nazw.

Matrix.cpp

using namespace Math; 
using namespace std; 

ostream& operator<< (ostream& os, const Matrix& obj) { 
    os << obj.getXYZ() << obj.getABC() << '\n'; 
    return os; 
} 

Z drugiej strony, jeśli funkcja przeciążenia robi należy dokonać przyjaciel tj potrzebuje dostępu do członków prywatnych i chronionych.

math.h

namespace Math { 
    class Matrix { 
     public: 
      friend std::ostream& operator<<(std::ostream&, const Matrix&); 
    }; 
} 

Trzeba ująć definicję funkcji z bloku przestrzeni nazw, a nie tylko using namespace Math;.

Matrix.cpp

using namespace Math; 
using namespace std; 

namespace Math { 
    ostream& operator<<(ostream& os, const Matrix& obj) { 
     os << obj.XYZ << obj.ABC << '\n'; 
     return os; 
    }     
} 
20

w C++ 14 można użyć następującej formy wydrukować dowolny obiekt, który ma T :: print (std :: ostream &) const; członek.

template<class T> 
auto operator<<(std::ostream& os, const T& t) -> decltype(t.print(os), os) 
{ 
    t.print(os); 
    return os; 
} 
+1

Właściwie to również działa w C++ 11. – jotik

+0

ciekawe rozwiązanie! Jedno pytanie - gdzie należy zadeklarować tego operatora, jak w skali globalnej? Zakładam, że powinien on być widoczny dla wszystkich typów, które można wykorzystać do jego templatowania? – barney

+0

@barney Może to być w twojej własnej przestrzeni nazw wraz z klasami, które go używają. – QuentinUK

Powiązane problemy