Funkcje operatorowe << i >> dzięki którym możesz zrobić takie coś:
Array tab = { 0, 1, 2, 3 };
cout << tab; // wypisze 0 1 2 3
Działa to jak zwykła funkcja:
ostream& write(ostream& out, const Array& arr) { /*wypisuje w pętli zawartość arr (cout << arr[i])*/ return out; }
write(cout,tab); // wywołanie
Tę część raczej rozumiesz, teraz dlaczego jest return. Dzięki niemu operatory << i >> można używać teoretycznie w nieskończoność.
cout << tab1 << tab2 << tab3;
cout << tab1 ma postać jak wcześniej pokazałem write(cout,tab) a więc zwraca ona obiekt ref ostream (cout w tym przypadku) dzięki czemu masz możliwość zrobienia czegoś takiego:
write(cout,tab).write(cout,tab2);
Wywołujesz funkcję na zwróconym obiekcie itd. A skoro postać z << jest odpowiednikiem write(), możliwy jest ten, przedstawiony wyżej, zapis:
cout << tab1 << tab2 << tab3;
Z pomocą tego możesz umieścić dowolny obiekt w cout lub cin, a nawet zrobić własny obiekt obsługujący podobne rzeczy:
MyConsole << 10 << " " << 10.58 << L" | coś tam";
Ogólnie przeciążając operatory można zrobić naprawdę wiele. Przykład z mojej biblioteki:
Vec2<T>& operator >>= (const T& value)
{
x>>=value; y>>=value; return *this;
}