"Essential C++": Providing Class Instances of the iostream Operators - c++

From Essential C++: 4.10 Providing Class Instances of the iostream Operators
Often, we wish to both read and write objects of a class. For example,
to display our trian class object, we want to be able to write
cout << train << endl;
To support this, we must provide an overloaded instance of the output
operator:
ostream& operator<< (ostream &os, const Triangular &rhs)
{
os << "(" << rhs.beg_pos() << "," << rhs.length() << ")";
rhs.display(rhs.length(), rhs.beg_pos(), os);
return os;
}
We return the same ostream object passed into the function. This
allows multiple outptu operators to be concatenated. Both objects are
passed by reference. The ostream operand is not declared as const
because each output operation modifies the internal state of the
ostream object.
I'm kind of confused why the ostream operand cannot be declared as const.
If the output operator is declared as the following:
const ostream& operator<< (const ostream &os, const Triangular &rhs)
Is there any problem with the above declaration?
Thanks

The problem is that if the ostream argument (or conversely istream) was a constant reference, then the operator would not be able to modify the stream object. Insertion/extraction to the streams modify the stream state, so the existing operator<< are non-const operations. That in turn means that while you can declare and even define:
std::ostream const & operator<<( std::ostream const & s, Type const & t );
The problem is that the definition would not be able to actually write anything at all into the stream:
std::ostream const & operator<<( std::ostream const & s, Type const & t ) {
s << "Hi"; // Error: operator<<( std::ostream&, const char*) requires a
// non-const `std::ostream&`
return s; // This is fine
}

When outputting the variable rhs, some data members inside ostream& os such as output buffer or position of file write if os is a ofstream must be modified.
Declaring os as const forbids such a modification.
And, as shown here, if os is declared as const, then you cannot use it to output primitive data types since none of ostream::operator<<() is declared as constant member function.

Yes, the ostream argument os is modified by calling <<.

Related

Changing an overloaded operator's signature

An operator can be declared only for the syntax defined for it in the C++ grammar. For example, one can’t define a unary % or a ternary +.
Consider the output operator for a class A. It has the following signature as a non-member:
ostream& operator<<(ostream&, A&);
This signature can't be changed. operator<< is a binary operator. Ie, it can take only 2 arguments.
The same is the case with the corresponding >> operator.
In certain situations, this can be restrictive, since it doesn't give the user of this operator, the required flexibility.
For example, consider a class Money used to store a monetary amount and its output operator:
ostream& operator<<(ostream&, Money&);
Since a monetary value is involved, we need to display the currency symbol also, which could be either the local or international symbol. If the user should be able to specify this, we would need the above operator to have another parameter, say bool intl.
The operator's signature would then be:
ostream& operator<<(ostream&, bool intl, Money&);
Of course, this isn't possible, since the signature is fixed.
How can we proceed in such a situation?
Thanks.
How about making some kind of tag-structure that can be used for overloading the operator? Like e.g.
ostream& operator<<(ostream&, LocalMonetaryType);
Then you can do e.g.
std::cout << LocalMonetaryTypeInstance << YourMoneyObject;
Could be refined so that you use a Money member function to get the tag object, and it then contains the expected symbol to be written.
If a Money is a class it should have a bool intl as a member field. So in that case operator will still have proper definition. It will be even better to define enum for that, example code:
class Money
{
public:
MonetaryEnum localCurrency;
double amount;
friend ostream& operator<< (ostream& os, const Money& m);
}
ostream& operator<<(ostream& os, const Money& m)
{
os << enumToString(localCurrency) << m.getAmount() << std::endl;
return os;
}
and you will be able to provide desired behaviour. It is only example :)

Explicitly calling templated overload of operator<< gives error

I have the following code:
//.hpp
enum class UIDCategory
{
GoodType //and others
};
typedef unsigned short UID;
typedef UID GoodType;
template<UIDCategory UIDCat> //Yes, UIDCat is supposed to go unused
inline std::ostream& operator<<(std::ostream& str, const UID& uid)
{
return str << uid;
}
std::ostream& operator<<(std::ostream& str, const Recipe::GoodRatio& goodRatio);
//definition of Rules here
template<>
inline std::ostream& operator<< <UIDCategory::GoodType>(std::ostream& str, const GoodType& goodType)
{
return str << Rules::goods.at(goodType);
}
//.cpp
std::ostream& operator<<(std::ostream& str, const Recipe::GoodRatio& goodRatio)
{
return str.template operator<< <UIDCategory::GoodType>(goodRatio.goodType);
}
I am using VC++17.
I get the following error on the line in the function in the .cpp file:
Rules.cpp(21): error C2677: binary '<': no global operator found which takes type 'UIDCategory' (or there is no acceptable conversion)
I have been searching the web for a solution and I found out that the template keyword is necessary in the call to operator<< <UIDCategory::GoodType>(goodRatio.goodType) to denote that operator<< is actually a template so I added it as shown but the error doesn't go away. What am I doing wrong here?
The whole idea here is to provide a work-around for the limitation that typedefs do not introduce a new type and therefore cannot be used in overload resolution. I ran into trouble when I simply introduced the following overload: std::ostream& operator<<(std::ostream& str, const GoodType& goodType). This header is equivalent to std::ostream& operator<<(std::ostream& str, const unsigned short& goodType) and so str << aGoodTypeis ambiguous (it clashes with the one in std).
My code is an attempt to enable the user to explicitly state what 'overload' of the << operator is to be used by making a templated overload of the << operator and then explicitly specializing it for the different members of UIDCategory.
I'd appreciate any help on both the error and on the thing that I'm trying to achieve.
While making my Minimal, Complete, and Verifiable example as suggested by Jonas I actually solved the problem. The problem was that I was using the wrong calling convention of the << operator.
I called it as if it is a member of the stream while it isn't.
So it should be operator<<<UIDCategory::GoodType>(str, goodRatio.goodType) instead of str.template operator<< <UIDCategory::GoodType>(goodRatio.goodType).
Also I decided that this is way to convoluted anyway for what I was trying to achieve and chose a simpler method with some minor drawbacks.

Overloading the stream insertion (<<) operator?

When we overload the stream insertion operator to work on user defined objects, we usually define it as a global friend function as follows:
ostream& operator << (ostream& out, const MyClass& x) {
// Do something
return out;
}
My question is, I believe the object cout (which is a global object) is passed as the first argument to this function. But, why? It's a global function, so it is accessible in this function anyway, why pass it as an argument. In other words, why not do the following:
ostream& operator << (const MyClass& x) {
// Do something
return cout;
}
There are two reasons.
One is semantic: the first argument need not be std::cout. It can be any std::ostream, be it std::cerr, a std::ofstream, a std::ostringstream, etc.
The second is syntactic: << necessarily takes two arguments, and there's no way to write an overload without two arguments (though the first argument can be a this argument).

Anybody explain the line of C++ code

std::ostream & _return = ::operator<<(_os, _event)
Especially I would like to know: What is the data type of _return and How can I print it in console.
std::ostream & _return = ::operator<<(_os, _event);
Especially I would like to know: What is the data type of _return and How can I print it in console.
The code looks for an operator<< at global scope (not in any namespace), which can accept the _os and _event objects as parameters. It's not necessarily true, but given "<<" is the normal way streaming output is done in C++, you can expect that it's probably going to be a function such as:
std::ostream& operator<<(std::ostream&, const Event&);
Where Event is whatever type the _event object has, and I've assumed _os will be some type derived from std::ostream, and consequently able to be handled by a reference to std::ostream.
Almost all such operator<< functions return their first stream argument, so your code is probably doing some output, then effectively assigning to _return as if it were:
std::ostream& _return = _os;
Here, the static type of _return itself is std::ostream& - a reference to a std::ostream (Standard output stream) object, but the run-time type will be whatever type _os has... that's the type of object that operations on _return will invoke polymorphically. This could be ofstream, ostringstream or many other types.
How can I print it in console.
There is no Standard way to get textual type names in C++, though runtime polymorphic types do have runtime type information that includes an optional textual field that's normally populated with a type name of some sort - whether it will have full namespace qualifiers, name mangling or whatever is unspecified, but you can try it easily enough:
std::cout << typeid(_return).name() << '\n';
(For GCC, see Unmangling the result of std::type_info::name for tips on unmangling such type names)
_return is just a variable of type "Reference to std::ostream class object".
And it is initialized with a return value of << operator in global namespace ::operator<<(ostream& os, const some_Obj_reference& ref).
Ttis could be as well
std::ostream & _return = (_os <<_event);

Why does operator>> (or <<) overloading function need to receive an i\ostream reference?

From cplusplus.com, I saw that ostream class's member function operator<< looks like this:
ostream& operator<< (bool val); ostream& operator<< (int val);
.... and so on.
It does make sense because when you use the Cout object like cout<<x you activate the ostream& operator<< (int val) function, so you actually use the << operator on Cout object. This is very much like every other operator and sends the int variable to the function. What is the difference and what exactly happens when I want to stream an object of my own? Why does the syntax is suddenly ostream& operator<< (**ostream &os**, object ob)?
Why do I need to add the ostream var? I am still using cout<<ob so whay isnt it just ostream& operator<< (object obj)? All I pass is my object. The cout object is allready there.
operator<< is generally defined as a free function; that is, not a member function. Since it is an overloaded binary operator, that means it get's its left argument first and its right argument second.
The operator<< traditionally returns a reference to its left argument to enable the idiomatic chain of output.
To make it obvious to the reader, I tend to define my operator overloads using the lhs and rhs abbreviations; an operator<< would look similar to this, for some type T.
std::ostream& operator<<(std::ostream& lhs, T const& rhs)
{
// TODO: Do something
return lhs;
}
As a member function
As with other binary it could be defined as a member function. That is, let us suppose that you with defining your own iostream. Amongst other things, your class declaration may look like this. Again, T is a particular type.
class MyIOStream : public iostream
{
public:
MyIOStream& operator<<(T const& rhs);
}
In this case operator<< is a member function. It has the same semantics when used as <<.
References
Operators in C and C++ - a great summary of all the operators you can overload and their typical arguments.
why do I need to add the ostream var?
I'm sure it's there so that you can chain outputs together:
cout << foo << bar
The first call, cout << foo will result in an ostream reference that can be used for the << bar part.
Some of the stream extractors are members of basic_istream; because they are members, the basic_istream argument is implied. Some of the stream extractors are not members of basic_istream. Because they are not members, the basic_istream argument has to be part of the declaration.
Like this (oversimplified):
class basic_istream {
public:
basic_istream& operator>>(int& i);
}
basic_istream& operator>>(basic_istream&, std::string& str);
Both can be called in the same way:
int i;
std::cin >> i; // calls basic_istream::operator>>(int&)
std::string str;
std::cin >> str; // calls operator>>(basic_istrea&, std::string&)