The function you sent (hereinafter referred to as the "function") is an overload of the insert operator << , operator << . It allows you to output an object of type Telegram or any other type received from Telegram or convertible to Telegram in the output stream. This is similar to the spirit of the general use of input / output streams in C ++:
std::cout << 0 << '\n';
Here you output int 0 and then a new char string to the standard output stream. With the function you posted, you can now do something like
Telegram tel; // if Telegram has a default constructor std::cout << tel << '\n';
something that you otherwise could not do, because the standard C ++ library does not know about your new Telegram type and therefore has never determined how to output objects of this type.
In code:
inline std::ostream& operator<<(std::ostream& os, const Telegram& t)
The first line begins with the inline . Presumably, the function is defined in the header file, and therefore in this case you should use the inline so that the definition does not violate one definition rule.
That is, every time you include a header in the implementation file, you get a function defined for this implementation file. When the linker comes in to link all the compiled object files, he will find several function definitions, one in each implementation file, which includes a header with the function you published. This is what is forbidden by C ++; C ++ requires that a function can be implemented no more than once and exactly once if you intend to call it.
Using the inline keyword essentially requires the C ++ compiler to make sure that the function is not defined more than once so that the linker jumps from its place and complains about the ambiguity of several definitions to choose from.
Here, I argue that it is a bad idea to embed a function. It would be better to remove the inline and transfer the function definition to your own translation unit or implementation file. Thus, the function will be compiled exactly once, and not once for each implementation file, which includes a header with the function.
You will notice later that a function is a free function, not a member function. This allows (requires, in fact) the function to specify the operator operand on the left, the stream object. This means that the function will work with any object that is converted to a stream. It also means that you do not need to modify the class to add this extension to display semantics. If the function is a member, you will have to change the class header, which in turn means recompiling all the implementation files containing this header. Of course, it seems that your function is defined in the header; this is probably a bad idea, as I explained above.
Further you will see that the function returns std::ostream . std::ostream is actually typedef'd as std::basic_ostream<char, std::char_traits<char> > , and therefore your function can only output Telegram objects to streams that work with char types.
The reason the function returns a std::ostream is because you can associate calls with the function. If you look carefully,
std::cout << 0 << 1;
it is actually a chain of two calls to the overload function of the insert statement. Once for output 0, and then for output 1. This is equivalent
std::operator<<(std::operator<<(std::cout, 0), 1);
The first call to insert 0 returns the output stream, and the second call insert 1 accepts the returned output stream and inserts 1. Therefore, we return the output stream: therefore, we can chain the calls.
You will also notice that the insert statement has associativity from left to right, which means that in the above statement, 0 is guaranteed to be displayed first and 1 second. This is in contrast to the equivalent line that I wrote above, in which associativity (function calls) is inside out. This gives much less readable IMO code and that is one of the benefits of using operator << for output semantics.
Next, see the function parameters. The first parameter a std::ostream is taken by reference. This will allow you to change the flow. This is also why the stream is not taken by the const reference.
The second parameter can be used with the const reference, because we do not want to change it, just read it. Nevertheless, we want to take it by reference, again because we do not intend to change it, not even for local purposes, and therefore preserving the copy structure can only be a good idea. It also allows you to take derivatives from Telegram and roll virtual functions to them if the need arises. In addition, when using the const link, you can display a temporary value, as in std::cout << Telegram() << '\n'; .
{ os << "time: " << t.DispatchTime << " Sender: " << t.Sender << " Receiver: " << t.Receiver << " Msg: " << t.Msg;
This code should now be clear. Presumably, each of the members that you insert into the output stream has an insert statement defined for. The standard library defines insertion into output streams for primitives, for strings, for complex numbers, and other standard types.
return os; }
And finally, you return a stream object, so the caller can associate the output with the output of another object. Note that you can simply return the result of the chain:
return os << "time: " << t.DispatchTime << " Sender: " << t.Sender << " Receiver: " << t.Receiver << " Msg: " << t.Msg;
Finally, to demonstrate the bonus that you get from using a free function and taking the Telegram parameter from the link, consider:
struct Telegram { Telegram(); Telegram(const Communication& c); virtual ~Telegram(); }; struct Message : public Telegram { }; struct Letter { operator Telegram() const; };
All using this single function to output the Telegram object.