Is this valid C++ code?

B

Brendon Costa

Is the following valid C++?

I expected the output (This is correct with MSVC 6 or GCC when the
<<std::flush is in the code, but only works with MSVC when the
<<std::flush is omitted) from below to be:
Hello 3
C2

The incorrect output when compiled with GCC (without <<std::flush)
looks something like:
0x4410003
672


Note:
With the "<< std::flush" all seems to work fine with GCC (On both
linux + windows using MinGW) and also works fine on windows with MSVC
6.

Without the "<< std::flush" this works with MSVC 6 but with GCC it
will not output as i expect, but will output the address of the string
literal (As though it is printing a const void* instead of a const
char*).

I also checked this with a borland compiler and it does the same as
the GCC compiler. Which makes me think that there is a bug in the
code... I was thinking that the ostringstream temporary may not exist
throughout the duration of the function call but i dont think that is
the case anymore.

Is there another way to do what i am trying to achieve?

Thanks,
Brendon.


#include <iostream>
#include <sstream>

// NOTE: The flush is because for some reason passing a string literal
// as the first parameter seemed to display the string literals
address
// as though it was formatting a const void* not a const char* string
literal
#define STR(Message) \
static_cast<std::eek:stringstream&>(std::eek:stringstream() \
<< std::flush \
<< Message).str()

void F(const std::string& s)
{
std::cout << s << std::endl;
}

int main()
{
F(STR("Hello " << 3 << " again"));
F(STR('C' << 2));

return 0;
}
 
M

michael.goossens

Different compilers react differently on some things, so its natural
that a gcc compiler reacts different than a VC one. I don't know
wheter there is a format which would run on all compilers though but
basicly I think you have to adjust to the compiler that you pick and
not insist that your code would be able to compile on every compiler.
 
K

Kai-Uwe Bux

Brendon said:
Is the following valid C++?

I expected the output (This is correct with MSVC 6 or GCC when the
<<std::flush is in the code, but only works with MSVC when the
<<std::flush is omitted) from below to be:
Hello 3
C2

The incorrect output when compiled with GCC (without <<std::flush)
looks something like:
0x4410003
672


Note:
With the "<< std::flush" all seems to work fine with GCC (On both
linux + windows using MinGW) and also works fine on windows with MSVC
6.

Without the "<< std::flush" this works with MSVC 6 but with GCC it
will not output as i expect, but will output the address of the string
literal (As though it is printing a const void* instead of a const
char*).

I also checked this with a borland compiler and it does the same as
the GCC compiler. Which makes me think that there is a bug in the
code... I was thinking that the ostringstream temporary may not exist
throughout the duration of the function call but i dont think that is
the case anymore.

Is there another way to do what i am trying to achieve?

Maybe, what is it that you are trying to achieve?
Thanks,
Brendon.


#include <iostream>
#include <sstream>

// NOTE: The flush is because for some reason passing a string literal
// as the first parameter seemed to display the string literals
address
// as though it was formatting a const void* not a const char* string
literal
#define STR(Message) \
static_cast<std::eek:stringstream&>(std::eek:stringstream() \
<< std::flush \
<< Message).str()

Note that

std::eek:stringstream() << "some message"

is equivalent to

operator<<( std::eek:stringstream(), "some message" );

or

std::eek:stringstream().operator<<( "some message" );

Since you cannot bind a temporary to a non-const reference [8.5.3], the
compiler rejects the first hypothesis. The second interpretation, however,
works after a little conversion. This is, why the code prints the address.


When you insert the std::flush you get

( std::eek:stringstream() << std::flush )

which returns a reference. This reference does bind, and the overall thingy
is interpreted as

operator<< ( ( std::eek:stringstream() << std::flush ), "some message" );

which yield the desired output.


For more details:

http://groups.google.com/group/comp.lang.c++/browse_frm/thread/9f553d5a01c08ed8/5439207b78579f4a


void F(const std::string& s)
{
std::cout << s << std::endl;
}

int main()
{
F(STR("Hello " << 3 << " again"));
F(STR('C' << 2));

return 0;
}


Best

Kai-Uwe Bux
 
A

Alf P. Steinbach

* Brendon Costa:
#include <iostream>
#include <sstream>

// NOTE: The flush is because for some reason passing a string literal
// as the first parameter seemed to display the string literals
address
// as though it was formatting a const void* not a const char* string
literal
#define STR(Message) \
static_cast<std::eek:stringstream&>(std::eek:stringstream() \
<< std::flush \
<< Message).str()

The effect of the flush is to give you a reference to an ostream object,
which is an lvalue, rather then the temporary stringstream() object,
which is an rvalue. The difference is that the lvalue can be passed to
formal argument ostream&, whereas the rvalue can't. And the relevant
operator<< is a free-standing operator.

The difference with MSVC 6.0 may be due to a different implementation
of operator<< than the one required by the standard.

void F(const std::string& s)
{
std::cout << s << std::endl;
}

int main()
{
F(STR("Hello " << 3 << " again"));
F(STR('C' << 2));

return 0;
}

Instead of using a macro and trickery, just use some existing library
solution, or define such yourself, e.g.

#include <iostream>
#include <string>
#include <sstream>

class Str
{
private:
std::eek:stringstream myStream;

Str( Str const& );
Str& operator=( Str const& );

public:
Str() {}

template< typename T >
Str& operator<<( T const& v )
{
myStream << v; return *this;
}

operator std::string() const
{
return myStream.str();
}
};

void f( std::string const& s)
{
std::cout << s << std::endl;
}

int main()
{
f( Str() << "Hello " << 3 << " again" );
f( Str() << 'C' << 2 );
}

Cheers, & hth.,

- Alf
 
B

Brendon Costa

Thanks for the responses and link to past topic. They were helpful in
understanding the problem. Also thanks for the alternate solution. It
is much nicer than using macros to achieve the result.

One thing with the original solution using std::flush that was also
brought up in the link that was provided is that the result of
"std::eek:stringstream() << std::flush" uses a temporary to call a member
which then returns a reference. This reference can then be used like a
lvalue. I dont want to do it, but is this a "valid" method to get
around the non-const reference to a temporary rule? Or will this
possibly have undefined behaviour?

Thanks,
Brendon.
 
K

Kai-Uwe Bux

Brendon said:
Thanks for the responses and link to past topic. They were helpful in
understanding the problem. Also thanks for the alternate solution. It
is much nicer than using macros to achieve the result.

One thing with the original solution using std::flush that was also
brought up in the link that was provided is that the result of
"std::eek:stringstream() << std::flush" uses a temporary to call a member
which then returns a reference. This reference can then be used like a
lvalue. I dont want to do it, but is this a "valid" method to get
around the non-const reference to a temporary rule? Or will this
possibly have undefined behaviour?

It's valid. E.g.,

class X {
...
public:

X & me ( void ) {
return ( *this );
}

X const & me ( void ) const {
return ( *this );
}

X & operator= ( X const & other ) {
swap( *this, X( other ).me() );
}

friend
void swap ( X & rhs, X & lhs ) {
...
}

};

However, I would guess that in most places such code does not conform to the
principle of least surprise.

Also, keep in mind that such a me() method can be treacherous: a temporary
is destroyed at the end of the most derived expression. Thus:

X & ref = X().me();

will yield a dangling reference the use of which has undefined behavior.


One situation where a me() method _can_ be usefull is to provide default
values for non-const reference parameters:

foo ( X & dummy = X().me() ) {
}

I once went through the trouble of checking the standard as to whether the
temporary is guaranteed to live through the function. I recall that I
answered that positively. However, I couldn't reproduce the deduction off
hand. It shows that such code is probably not-so-good from a maintenance
point of view.


Best

Kai-Uwe Bux
 
B

Brendon Costa

It's valid. E.g.,

Thanks for the clarification.

However, I would guess that in most places such code does not conform to the
principle of least surprise.

I agree with you here. I was reluctant to use it as it was, which i
dont have to thanks to the help i have recieved. If i did not know why
it worked the way it worked then i knew it is a bad idea to keep it in
the code base as others would have even more trouble trying to
understand if they needed to browse the code themselves.


Thanks again everyone for the help.
Brendon.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,994
Messages
2,570,222
Members
46,809
Latest member
moe77

Latest Threads

Top