Discussion:
Operator overloading
(too old to reply)
Sabetay Toros
2008-06-09 18:41:03 UTC
Permalink
Hi,


class Complex
{

double re, im;
...

ostrstream& operator<<(ostrstream& os, Complex& Complex);

}

I'm a little confused, please could someone explain If it is legal to
overload the above operator<< function, why do we need to supply the
Complex argument. Isn't it the the same complex argument object, which
we are operating on?

in other words which of the following lines must be written?


ostrstream& Complex::operator<<(ostrsream& os, Complex& Cmp)
{
return os << re << im;
or
return os << Cmp.re << Cmp.im;
}

Thanks

Sabetay
Remy Lebeau (TeamB)
2008-06-09 19:47:05 UTC
Permalink
Post by Sabetay Toros
I'm a little confused, please could someone explain If it is
legal to overload the above operator<< function, why do
we need to supply the Complex argument.
You don't supply the second parameter when defining the operator as a member
of the class. You only need to provide the second parameter when defining
the operator globally.

However, in this particular situation, if you want to stream a Complex
object into an ostrstream, you will have to define the '<<' operator
globally, not a class member. Or define a '>>' operator as a class memeber.
By defining the '<<' operator as a class member, you define which types can
be streamed into a Complex object instead (such as streaming in an
istrstream), which is not what you were trying to do.

For example:

class Complex
{
public:
double re;
double im;
...
Complex& operator<<(istrstream& os);

ostrstream& operator>>(ostrstream& os);
};

Complex& Complex::operator<<(istrstream& is)
{
is >> re >> im;
return *this;
}

ostrstream& Complex::operator>>(ostrstream& os)
{
return os << re << im;
}

// note: not a class member!
ostrstream& operator<<(ostrstream& os, const Complex& Cmp)
{
return os << Cmp.re << Cmp.im;
}

For streaming out to a ostrstream, you can do one of the following now:

ostrstream os
Complex cmp;
...
os << cmp;
or
cmp >> os;


Gambit
Sabetay Toros
2008-06-10 08:18:08 UTC
Permalink
Post by Remy Lebeau (TeamB)
Post by Sabetay Toros
I'm a little confused, please could someone explain If it is
legal to overload the above operator<< function, why do
we need to supply the Complex argument.
You don't supply the second parameter when defining the operator as a member
of the class. You only need to provide the second parameter when defining
the operator globally.
However, in this particular situation, if you want to stream a Complex
object into an ostrstream, you will have to define the '<<' operator
globally, not a class member. Or define a '>>' operator as a class memeber.
By defining the '<<' operator as a class member, you define which types can
be streamed into a Complex object instead (such as streaming in an
istrstream), which is not what you were trying to do.
class Complex
{
double re;
double im;
...
Complex& operator<<(istrstream& os);
ostrstream& operator>>(ostrstream& os);
};
Complex& Complex::operator<<(istrstream& is)
{
is >> re >> im;
return *this;
}
ostrstream& Complex::operator>>(ostrstream& os)
{
return os << re << im;
}
// note: not a class member!
ostrstream& operator<<(ostrstream& os, const Complex& Cmp)
{
return os << Cmp.re << Cmp.im;
}
ostrstream os
Complex cmp;
...
os << cmp;
or
cmp >> os;
Gambit
Remy,

I've tried what you suggest with no success. I'm getting the following
error.

class Complex
{
public:
double re;
double im;

Complex& operator<<(istrstream& os);

ostrstream& operator>>(ostrstream& os);
};

Complex& Complex::operator<<(istrstream& is)
{
is >> re >> im;
return *this;
}

ostrstream& Complex::operator>>(ostrstream& os)
{
return os << re << im; // it is giving the error to this line
}



[C++ Error] Complex.h(27): E2357 Reference initialized with 'ostream',
needs lvalue of type 'ostrstream'

Stopping precompiling headers.
Remy Lebeau (TeamB)
2008-06-10 16:27:31 UTC
Permalink
Post by Sabetay Toros
I've tried what you suggest with no success. I'm getting
the following error.
<snip>
Post by Sabetay Toros
[C++ Error] Complex.h(27): E2357 Reference initialized
with 'ostream', needs lvalue of type 'ostrstream'
The '<<' operator for ostrstream returns an ostream rather than ostrstream,
so change your overloaded operator accordingly:

ostream& Complex::operator>>(ostrstream& os)
{
return os << re << im;
}

You should probably change the input parameter to ostream as well:

ostream& Complex::operator>>(ostream& os)
{
return os << re << im;
}


Gambit
Hendrik Schober
2008-06-10 17:46:53 UTC
Permalink
Post by Remy Lebeau (TeamB)
Post by Sabetay Toros
I've tried what you suggest with no success. I'm getting
the following error.
<snip>
Post by Sabetay Toros
[C++ Error] Complex.h(27): E2357 Reference initialized
with 'ostream', needs lvalue of type 'ostrstream'
The '<<' operator for ostrstream returns an ostream rather than ostrstream,
ostream& Complex::operator>>(ostrstream& os)
operator<<
Post by Remy Lebeau (TeamB)
{
return os << re << im;
}
ostream& Complex::operator>>(ostream& os)
operator<<
Post by Remy Lebeau (TeamB)
{
return os << re << im;
}
Gambit
Schobi
(who had been bitten by this typo several times)
--
***@gmx.de is never read
I'm HSchober at gmx dot de
"I guess at some point idealism meets human nature and
explodes." Daniel Orner
Alan Bellingham
2008-06-11 09:12:49 UTC
Permalink
Post by Sabetay Toros
Post by Remy Lebeau (TeamB)
ostream& Complex::operator>>(ostream& os)
operator<<
Post by Remy Lebeau (TeamB)
{
return os << re << im;
}
I think Remy was trying to allow the use of the streaming operator as a
non-static member function. Since the class itself then has to be the
left parameter, the call has to be back to front to normal:

Complex c;
c >> stream;

rather than

Complex c;
stream << c;

Given the arguments about overloading << and >> anyway, and the fact
that this form also prevents method chaining, I think it's somewhat
wrong-headed: you'd be better off calling the function writeTo() or
similar.

Moral - never try to make the streaming operators non-static members.

Alan Bellingham
--
Team Browns
ACCU Conference 2009: to be announced
Remy Lebeau (TeamB)
2008-06-11 16:22:48 UTC
Permalink
Post by Alan Bellingham
I think Remy was trying to allow the use of the streaming operator as a
non-static member function.
I showed examples of both a non-static member and a global function.
Post by Alan Bellingham
Complex c;
c >> stream;
That is the example for the non-static member.
Post by Alan Bellingham
Complex c;
stream << c;
That is the example for the global function.


Gambit
Chris Uzdavinis (TeamB)
2008-06-11 17:58:24 UTC
Permalink
Post by Remy Lebeau (TeamB)
Post by Alan Bellingham
Complex c;
c >> stream;
That is the example for the non-static member.
Yes it is, but is an example of what not to do. Such an example is
unconventionally backwards, and really makes for some ugly code.

For example, what if you wanted to stream two things out together....
Doesn't code like this make you just want to shoot somebody:

c >> stream << d << e;

?
Post by Remy Lebeau (TeamB)
Post by Alan Bellingham
Complex c;
stream << c;
That is the example for the global function.
Yes, and THAT is correct syntax too. (Not correct in the
language/compiler sense, but in terms of conventional usage and
the principle of least surprise.)
--
Chris (TeamB);
Hendrik Schober
2008-06-12 09:28:10 UTC
Permalink
Post by Alan Bellingham
Post by Sabetay Toros
Post by Remy Lebeau (TeamB)
ostream& Complex::operator>>(ostream& os)
operator<<
Post by Remy Lebeau (TeamB)
{
return os << re << im;
}
I think Remy was trying to allow the use of the streaming operator as a
non-static member function. [...]
OMG.
Post by Alan Bellingham
Alan Bellingham
Schobi

Sergiy Kanilo
2008-06-10 02:55:46 UTC
Permalink
Post by Sabetay Toros
in other words which of the following lines must be written?
ostrstream& Complex::operator<<(ostrsream& os, Complex& Cmp)
{
return os << re << im;
or
return os << Cmp.re << Cmp.im;
}
return os << Cmp.re << " " << Cmp.im;

Cheers,
Serge
Hendrik Schober
2008-06-10 09:08:19 UTC
Permalink
Post by Sabetay Toros
Hi,
class Complex
{
double re, im;
...
ostrstream& operator<<(ostrstream& os, Complex& Complex);
}
I'm a little confused [...]
So am I by the above code.
It declares an 'operator<<()' as a /member/ function. Thus
that operator would take three arguments: A 'Complex' (aka
the '*this' argument implicit to all member functions), an
'ostrstream', and another 'Complex'. However, 'operator<<()'
only ever takes two arguments -- so the code cannot compile.
Let's sort this out.

First, on the left side of an '<<' operator invocation (when
used for streaming) is a stream. Since for member functions
the leftmost argument always is '*this', if you want to
implement a stream operator as a member function, it needs
to implemented as a member of the stream class. If you want
to use the standard library's stream classes, you cannot do
this. Stream custom operators are thus best implemented as
free functions.[1]
Also, the right-hand side object of a stream operator is not
modified. Therefor it should be passed as a 'const' parameter.
Otherwise you won't be able to stream out a 'const' Complex
object for no good reason.
Further, writing into a string stream is not different from
writing into a file stream or any other 'std::ostream'. So
why not use 'std::ostream' on the left side? It allows you
to pass in a string stream as well as any other output stream.
You gain more general usability with no effort.
Last, but not least, 'std::ostrstream' seems not a very good
choice as a string stream class. It's deprecated, and there
are very good reasons for it. Generally, you'd better use
'std::ostringstream' from the header <sstream>. (However,
according to the paragraph immediately above this one, this
decision is best left to the /users/ of your stream operator,
not the implementer, so it's actually moot to discuss it here.)

If we combine this, we have
class Complex;
std::ostream operator<<(std::ostream& os, const Complex& c);
(which could be implemented as Sergiy suggested).
Post by Sabetay Toros
Sabetay
Schobi

[1] My rule of thumb for binary operator that can, syntactically,
be implemented either as a member function or a free function:
If it modifies its left argument, make it a member, otherwise
make it a free function.
--
***@gmx.de is never read
I'm HSchober at gmx dot de
"I guess at some point idealism meets human nature and
explodes." Daniel Orner
Vladimir Grigoriev
2008-06-10 11:10:59 UTC
Permalink
Post by Hendrik Schober
If we combine this, we have
class Complex;
std::ostream operator<<(std::ostream& os, const Complex& c);
(which could be implemented as Sergiy suggested).
Post by Sabetay Toros
Sabetay
Schobi
[1] My rule of thumb for binary operator that can, syntactically,
If it modifies its left argument, make it a member, otherwise
make it a free function.
I'd like to add for what that was said by Hendrik that because in your
Complex class data members double re and double im were defined as private
then in this case you have a choice either to declare the operator<< () as
friend for the class and directly manipulate of 're' and 'im' in the
operator function or to declare the operator<<() as global function outside
the class but inside the class to declare public methods for accessing 're'
and 'me'.

For example you may declare the operator either such way

class Complex
{
private: // I advise always do specify private clause even if it is a
default clause for classes
double re, im;
...
public:
friend ostream & operator<<( ostream & os, const Complex & c )
{
os << "<" << c.re << ", " << c.im << ">";
return os;
}
...
};

or another way

class Complex
{
private:
double re,im;
...
public:
double real() const { return re; }
double image() const { return im; }
...
};

ostream & operator( ostream & os, const Complex & c )
{
os << "<" << c.real() << ", " << c.image() << ">";
return os;
}

Vladimir Grigoriev
Alan Bellingham
2008-06-10 11:40:48 UTC
Permalink
Post by Sabetay Toros
class Complex
{
private: // I advise always do specify private clause even if it is a
default clause for classes
I'd go further and advise you to put the public part first. That way,
anyone reading the class definition can stop once they've finished with
the part that's relevant to them.

Alan Bellingham
--
Team Browns
ACCU Conference 2009: to be announced
Chris Uzdavinis (TeamB)
2008-06-10 13:39:53 UTC
Permalink
Post by Alan Bellingham
Post by Sabetay Toros
class Complex
{
private: // I advise always do specify private clause even if it is a
default clause for classes
I'd go further and advise you to put the public part first. That way,
anyone reading the class definition can stop once they've finished with
the part that's relevant to them.
Me too. The public part is the class interface to users. THe private
parts are implementation details, which are of great concern to the
class author, but of little concern to the class's users.
--
Chris (TeamB);
Hendrik Schober
2008-06-10 20:44:24 UTC
Permalink
Post by Sabetay Toros
class Complex
{
private: // I advise always do specify private clause even if it is a
default clause for classes
I'd go further and advise you to put the public part first. [...]
Me too. [...]
AOL.

Schobi
--
***@gmx.de is never read
I'm HSchober at gmx dot de
"I guess at some point idealism meets human nature and
explodes." Daniel Orner
Hendrik Schober
2008-06-10 20:46:45 UTC
Permalink
Post by Vladimir Grigoriev
[...]
ostream & operator( ostream & os, const Complex & c )
{
os << "<" << c.real() << ", " << c.image() << ">";
return os;
}
operator<<

(Ever thought about upgrading your brain compiler?)
Post by Vladimir Grigoriev
Vladimir Grigoriev
Schobi
--
***@gmx.de is never read
I'm HSchober at gmx dot de
"I guess at some point idealism meets human nature and
explodes." Daniel Orner
Loading...