By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
448,499 Members | 1,327 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 448,499 IT Pros & Developers. It's quick & easy.

behaviour change in operator<<

P: n/a
I found a change in the following code, that behaved
correctly in VC++ 6.

#include <strstream>
using namespace std;
void main()
{
char x[100];
ostrstream(x, 100) << "pippo" << "pluto" << ends;
// here x contains "004400C8pluto"
}

Clearly,

basic_ostream::operator<<(const void*)
have been called, instead of
basic_ostream::operator<<(const char*).

Some change in scoping rule (due to the introduction of
Koenig Lookup) justify this change?

Many Thanks for your attention.

Nov 16 '05 #1
Share this Question
Share on Google+
3 Replies


P: n/a
Carlo Capelli wrote:
I found a change in the following code, that behaved
correctly in VC++ 6.

#include <strstream>
using namespace std;
void main()
{
char x[100];
ostrstream(x, 100) << "pippo" << "pluto" << ends;
// here x contains "004400C8pluto"
}

Clearly,

basic_ostream::operator<<(const void*)
have been called, instead of
basic_ostream::operator<<(const char*).

Some change in scoping rule (due to the introduction of
Koenig Lookup) justify this change?

Many Thanks for your attention.


It's not a bug. There is a member output operator for void*, but the char*,
signed char*, and unsigned char* output operators are non-members whose
first parameter is a non-const ostream&. The compiler is choosing the member
operator, because you can call member functions through temporary objects,
e.g. X().f(), or here, ostream().operator<<("pippo"), but you can't bind
temporaries to non-const references, which eliminates the non-member
operators. So:

ostrstream(x, 100) << "pippo" << "pluto" << ends;

calls the operator<<(const void*) member and stores a pointer value[1],
while:

ostrstream os(x, 100);
os << "pippo" << "pluto" << ends;

calls the non-member operator<<(ostream&, const char*) and stores the
strings.

The craziest thing is that this is apparently the way it's supposed to work.
The non-member operators<< are rejected in the first case as non-viable,
because selecting them would require binding a temporary to a non-const
reference, leaving the member operator<<(const void*) to handle the output
of the string literal. FWIW, I'm not convinced it makes sense to eliminate
functions from overload resolution based on the reference binding issue, but
that's what the standard says.

[1] The string "pluto" is stored after the address of "pippo", because
operator<< returns ostream&, and there's no problem binding it to the first
parameter of operator<<(ostream&, const char*).

--
Doug Harrison
Microsoft MVP - Visual C++
Nov 16 '05 #2

P: n/a
>From: "Doug Harrison [MVP]" <ds*@mvps.org>
Subject: Re: behaviour change in operator<<
Date: Wed, 13 Aug 2003 13:23:36 -0500
Message-ID: <qr********************************@4ax.com>
References: <0a****************************@phx.gbl>

Carlo Capelli wrote:
I found a change in the following code, that behaved
correctly in VC++ 6.

#include <strstream>
using namespace std;
void main()
{
char x[100];
ostrstream(x, 100) << "pippo" << "pluto" << ends;
// here x contains "004400C8pluto"
}

Clearly,

basic_ostream::operator<<(const void*)
have been called, instead of
basic_ostream::operator<<(const char*).

Some change in scoping rule (due to the introduction of
Koenig Lookup) justify this change?

Many Thanks for your attention.
It's not a bug. There is a member output operator for void*, but the char*,
signed char*, and unsigned char* output operators are non-members whose
first parameter is a non-const ostream&. The compiler is choosing the

memberoperator, because you can call member functions through temporary objects,
e.g. X().f(), or here, ostream().operator<<("pippo"), but you can't bind
temporaries to non-const references, which eliminates the non-member
operators. So:

ostrstream(x, 100) << "pippo" << "pluto" << ends;

calls the operator<<(const void*) member and stores a pointer value[1],
while:

ostrstream os(x, 100);
os << "pippo" << "pluto" << ends;

calls the non-member operator<<(ostream&, const char*) and stores the
strings.

The craziest thing is that this is apparently the way it's supposed to work.The non-member operators<< are rejected in the first case as non-viable,
because selecting them would require binding a temporary to a non-const
reference, leaving the member operator<<(const void*) to handle the output
of the string literal. FWIW, I'm not convinced it makes sense to eliminate
functions from overload resolution based on the reference binding issue, butthat's what the standard says.

[1] The string "pluto" is stored after the address of "pippo", because
operator<< returns ostream&, and there's no problem binding it to the first
parameter of operator<<(ostream&, const char*).

--
Doug Harrison
Microsoft MVP - Visual C++


Actually there is a bug here :-( You can only call const member functions
on a temporary
object of a class type - and as all the ostream::operator<< functions are
non-const and all the
global operator<< require a ostream& as the first parameter the compiler
should not be able to
call any of the available functions. This code should not compile: I quick
check with the Comeau
indicates that the EDG compiler does indeed reject this code.

I've added the issue to our bug-database.

--
Jonathan Caves, Visual C++ Team
This posting is provided AS IS with no warranties, and confers no rights.

Nov 16 '05 #3

P: n/a
>From: "Doug Harrison [MVP]" <ds*@mvps.org>
Subject: Re: behaviour change in operator<<
Date: Wed, 13 Aug 2003 14:52:21 -0500
Message-ID: <sq********************************@4ax.com>
References: <0a****************************@phx.gbl> <qr********************************@4ax.com>
<tm**************@cpmsftngxa06.phx.gbl>
Jonathan Caves [MSFT] wrote:
Actually there is a bug here :-( You can only call const member functions
on a temporary
object of a class type
Perhaps strangely, that's incorrect. Things like this are perfectly legal:

struct X
{
X& operator=(const X&);
};

void f()
{
X() = X();
}

The non-const reference binding issue is solely responsible for this
weirdness:

void g(X&);

void h()
{
g(X()); // 1. Bad!
g(X() = X()); // 2. Fine!
}

Now add:

void g(const X&);

to the above and try again. You'll see that (1) now compiles, but note that
it calls the new overload which takes a const X&. On the other hand, (2)
calls the original g which takes plain X&. This is what I find hard to
justify for class types. It seems to me that a non-const X should bind to
X&, whether it's an rvalue or not. IIRC, when I experimented with this in
VC6 a couple of years ago, that's the rule VC6 implemented, while still
forbidding the binding for built-in types. I think that makes a lot of
sense, but it isn't standard. (NB: When using VC, compile the fragments

with-Za to get standard conformant behavior, which is what I've assumed above.)

Sigh: you are correct Doug: somehow in reducing the test case to a more
simple example I
changed it enough that the code did not compile. I admit the behavior does
appear strange
but I've convinced myself that it is allowed by the Standard. So you are
correct we don't
have a bug.
I've added the issue to our bug-database.


I think you can unbug it. :)


It's been debugged :-)

--
Jonathan Caves, Visual C++ Team
This posting is provided AS IS with no warranties, and confers no rights.

Nov 16 '05 #4

This discussion thread is closed

Replies have been disabled for this discussion.