Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Bad use of stringstream temporary?

Reply
Thread Tools

Bad use of stringstream temporary?

 
 
K. Frank
Guest
Posts: n/a
 
      03-24-2011
Hello Group!

The basic question is whether the following line of code
is legal and good (according to the current standard):

std::string s1 =
dynamic_cast<std::stringstream&>(std::stringstream () << "abc").str();

The problem is that "abc" gets rendered as a hex pointer
value, rather than as "abc".

This is a follow-up on my posting on the MinGW Users List.
You can find that thread, with more detail and variations
on the theme, here:

http://thread.gmane.org/gmane.comp.gnu.mingw.user/35926

The problem is that we (or at least I) haven't been able
to identify any specific error in the code, yet three
different compilers print out the "erroneous" pointer-value
result (several versions of mingw g++, Comeau 4.3.10.1, and
msvc 9).

One speculation is that the code is illegal under the
current standard, but legal under c++0x. It is the
case that mingw g++ 4.5 and 4.6 print out the "correct"
result of "abc" when using the "-std=c++0x" option.
(mingw g++ 4.4 still prints out the "incorrect" pointer
value with "-std=c++0x".)

Here is a short test program and its output:


<< stringstream_test.cpp >>

#include <iostream>
#include <sstream>
#include <typeinfo>
int main (int argc, char *argv[]) {

std::string s1 =
dynamic_cast<std::stringstream&>(std::stringstream () << "abc").str();
std::cout << "expecting abc: " << s1 << std::endl; // prints out
"abc" as a pointer

std::stringstream sstr;
std::string s2 = dynamic_cast<std::stringstream&>(sstr <<
"xyz").str();
std::cout << "expecting xyz: " << s2 << std::endl;

}


C:\>g++ -o stringstream_test stringstream_test.cpp

C:\>stringstream_test
expecting abc: 0x477024
expecting xyz: xyz


That is, the two very similar test cases give different
results. The version with the unnamed temporary
stringstream prints out the "incorrect" pointer value,
while the version with the named local variable works
as expected.

(Again, more variations on this sample program that fail
to compile or give unexpected results can be found in the
thread on the MinGW Users List linked to above.)

So, is this code in error (and, if so, what's the specific
problem)? Or have we stumbled across a cluster of similar
bugs in a number of different compilers / libraries?

Thanks for your insight.


K. Frank
 
Reply With Quote
 
 
 
 
Victor Bazarov
Guest
Posts: n/a
 
      03-24-2011
On 3/24/2011 12:08 PM, K. Frank wrote:
> The basic question is whether the following line of code
> is legal and good (according to the current standard):
>
> std::string s1 =
> dynamic_cast<std::stringstream&>(std::stringstream ()<< "abc").str();
>
> The problem is that "abc" gets rendered as a hex pointer
> value, rather than as "abc".
>
> This is a follow-up on my posting on the MinGW Users List.
> You can find that thread, with more detail and variations
> on the theme, here:
>
> http://thread.gmane.org/gmane.comp.gnu.mingw.user/35926
>
> The problem is that we (or at least I) haven't been able
> to identify any specific error in the code, yet three
> different compilers print out the "erroneous" pointer-value
> result (several versions of mingw g++, Comeau 4.3.10.1, and
> msvc 9).
>
> One speculation is that the code is illegal under the
> current standard, but legal under c++0x. It is the
> case that mingw g++ 4.5 and 4.6 print out the "correct"
> result of "abc" when using the "-std=c++0x" option.
> (mingw g++ 4.4 still prints out the "incorrect" pointer
> value with "-std=c++0x".)
>
> Here is a short test program and its output:
>
>
> << stringstream_test.cpp>>
>
> #include<iostream>
> #include<sstream>
> #include<typeinfo>
> int main (int argc, char *argv[]) {
>
> std::string s1 =
> dynamic_cast<std::stringstream&>(std::stringstream ()<< "abc").str();
> std::cout<< "expecting abc: "<< s1<< std::endl; // prints out
> "abc" as a pointer
>
> std::stringstream sstr;
> std::string s2 = dynamic_cast<std::stringstream&>(sstr<<
> "xyz").str();
> std::cout<< "expecting xyz: "<< s2<< std::endl;
>
> }
>
>
> C:\>g++ -o stringstream_test stringstream_test.cpp
>
> C:\>stringstream_test
> expecting abc: 0x477024
> expecting xyz: xyz
>
>
> That is, the two very similar test cases give different
> results. The version with the unnamed temporary
> stringstream prints out the "incorrect" pointer value,
> while the version with the named local variable works
> as expected.
>
> (Again, more variations on this sample program that fail
> to compile or give unexpected results can be found in the
> thread on the MinGW Users List linked to above.)
>
> So, is this code in error (and, if so, what's the specific
> problem)? Or have we stumbled across a cluster of similar
> bugs in a number of different compilers / libraries?


The only function possible is the member function operator<< with the
argument that is void*. The non-member operator<<(stream&, const char*)
cannot be used because there is no binding of a temporary to a reference
to non-const.

The behavior is as expected.

V
--
I do not respond to top-posted replies, please don't ask
 
Reply With Quote
 
 
 
 
Jeff Flinn
Guest
Posts: n/a
 
      03-24-2011
Victor Bazarov wrote:
> On 3/24/2011 12:08 PM, K. Frank wrote:
>> The basic question is whether the following line of code
>> is legal and good (according to the current standard):
>>
>> std::string s1 =
>> dynamic_cast<std::stringstream&>(std::stringstream ()<< "abc").str();
>>
>> The problem is that "abc" gets rendered as a hex pointer
>> value, rather than as "abc".
>>
>> This is a follow-up on my posting on the MinGW Users List.
>> You can find that thread, with more detail and variations
>> on the theme, here:
>>
>> http://thread.gmane.org/gmane.comp.gnu.mingw.user/35926
>>
>> The problem is that we (or at least I) haven't been able
>> to identify any specific error in the code, yet three
>> different compilers print out the "erroneous" pointer-value
>> result (several versions of mingw g++, Comeau 4.3.10.1, and
>> msvc 9).
>>
>> One speculation is that the code is illegal under the
>> current standard, but legal under c++0x. It is the
>> case that mingw g++ 4.5 and 4.6 print out the "correct"
>> result of "abc" when using the "-std=c++0x" option.
>> (mingw g++ 4.4 still prints out the "incorrect" pointer
>> value with "-std=c++0x".)
>>
>> Here is a short test program and its output:
>>
>>
>> << stringstream_test.cpp>>
>>
>> #include<iostream>
>> #include<sstream>
>> #include<typeinfo>
>> int main (int argc, char *argv[]) {
>>
>> std::string s1 =
>> dynamic_cast<std::stringstream&>(std::stringstream ()<< "abc").str();
>> std::cout<< "expecting abc: "<< s1<< std::endl; // prints out
>> "abc" as a pointer
>>
>> std::stringstream sstr;
>> std::string s2 = dynamic_cast<std::stringstream&>(sstr<<
>> "xyz").str();
>> std::cout<< "expecting xyz: "<< s2<< std::endl;
>>
>> }
>>
>>
>> C:\>g++ -o stringstream_test stringstream_test.cpp
>>
>> C:\>stringstream_test
>> expecting abc: 0x477024
>> expecting xyz: xyz
>>
>>
>> That is, the two very similar test cases give different
>> results. The version with the unnamed temporary
>> stringstream prints out the "incorrect" pointer value,
>> while the version with the named local variable works
>> as expected.
>>
>> (Again, more variations on this sample program that fail
>> to compile or give unexpected results can be found in the
>> thread on the MinGW Users List linked to above.)
>>
>> So, is this code in error (and, if so, what's the specific
>> problem)? Or have we stumbled across a cluster of similar
>> bugs in a number of different compilers / libraries?

>
> The only function possible is the member function operator<< with the
> argument that is void*. The non-member operator<<(stream&, const char*)
> cannot be used because there is no binding of a temporary to a reference
> to non-const.
>
> The behavior is as expected.


IIRC,

std::string s = (std:stringstream() << std::flush << "abc").string();

behaves as the OP expected, at least on MSVC8 and gcc4.0.1 under XCode
3.1.2.

Jeff
 
Reply With Quote
 
Victor Bazarov
Guest
Posts: n/a
 
      03-24-2011
On 3/24/2011 3:13 PM, Jeff Flinn wrote:
> Victor Bazarov wrote:
>> On 3/24/2011 12:08 PM, K. Frank wrote:
>>> The basic question is whether the following line of code
>>> is legal and good (according to the current standard):
>>>
>>> std::string s1 =
>>> dynamic_cast<std::stringstream&>(std::stringstream ()<< "abc").str();
>>>
>>> The problem is that "abc" gets rendered as a hex pointer
>>> value, rather than as "abc".
>>>
>>> This is a follow-up on my posting on the MinGW Users List.
>>> You can find that thread, with more detail and variations
>>> on the theme, here:
>>>
>>> http://thread.gmane.org/gmane.comp.gnu.mingw.user/35926
>>>
>>> The problem is that we (or at least I) haven't been able
>>> to identify any specific error in the code, yet three
>>> different compilers print out the "erroneous" pointer-value
>>> result (several versions of mingw g++, Comeau 4.3.10.1, and
>>> msvc 9).
>>>
>>> One speculation is that the code is illegal under the
>>> current standard, but legal under c++0x. It is the
>>> case that mingw g++ 4.5 and 4.6 print out the "correct"
>>> result of "abc" when using the "-std=c++0x" option.
>>> (mingw g++ 4.4 still prints out the "incorrect" pointer
>>> value with "-std=c++0x".)
>>>
>>> Here is a short test program and its output:
>>>
>>>
>>> << stringstream_test.cpp>>
>>>
>>> #include<iostream>
>>> #include<sstream>
>>> #include<typeinfo>
>>> int main (int argc, char *argv[]) {
>>>
>>> std::string s1 =
>>> dynamic_cast<std::stringstream&>(std::stringstream ()<< "abc").str();
>>> std::cout<< "expecting abc: "<< s1<< std::endl; // prints out
>>> "abc" as a pointer
>>>
>>> std::stringstream sstr;
>>> std::string s2 = dynamic_cast<std::stringstream&>(sstr<<
>>> "xyz").str();
>>> std::cout<< "expecting xyz: "<< s2<< std::endl;
>>>
>>> }
>>>
>>>
>>> C:\>g++ -o stringstream_test stringstream_test.cpp
>>>
>>> C:\>stringstream_test
>>> expecting abc: 0x477024
>>> expecting xyz: xyz
>>>
>>>
>>> That is, the two very similar test cases give different
>>> results. The version with the unnamed temporary
>>> stringstream prints out the "incorrect" pointer value,
>>> while the version with the named local variable works
>>> as expected.
>>>
>>> (Again, more variations on this sample program that fail
>>> to compile or give unexpected results can be found in the
>>> thread on the MinGW Users List linked to above.)
>>>
>>> So, is this code in error (and, if so, what's the specific
>>> problem)? Or have we stumbled across a cluster of similar
>>> bugs in a number of different compilers / libraries?

>>
>> The only function possible is the member function operator<< with the
>> argument that is void*. The non-member operator<<(stream&, const
>> char*) cannot be used because there is no binding of a temporary to a
>> reference to non-const.
>>
>> The behavior is as expected.

>
> IIRC,
>
> std::string s = (std:stringstream() << std::flush << "abc").string();
>
> behaves as the OP expected, at least on MSVC8 and gcc4.0.1 under XCode
> 3.1.2.


You meant .str(), not .string(), I am sure, and yes, it does.
Outputting 'std::flush' (which is a pointer to function) uses a member
op<<, which returns a ref to a non-const stream which then can be passed
to a non-member op<< to output the characters.

V
--
I do not respond to top-posted replies, please don't ask
 
Reply With Quote
 
K. Frank
Guest
Posts: n/a
 
      03-24-2011
Hello Group!

First off, thanks to all who responded.

On Mar 24, 3:13*pm, Jeff Flinn <(E-Mail Removed)> wrote:
> Victor Bazarov wrote:
> > On 3/24/2011 12:08 PM, K. Frank wrote:
> >> The basic question is whether the following line of code
> >> is legal and good (according to the current standard):

>
> >> * * std::string s1 =
> >> dynamic_cast<std::stringstream&>(std::stringstream ()<< *"abc").str();

>
> >> The problem is that "abc" gets rendered as a hex pointer
> >> value, rather than as "abc".

> ...
> > The only function possible is the member function operator<< with the
> > argument that is void*. *The non-member operator<<(stream&, const char*)
> > cannot be used because there is no binding of a temporary to a reference
> > to non-const.

>
> > The behavior is as expected.


Yes, this explanation seems to be the consensus. As I understand it,
the code is perfectly legal, but, because of the temporary
stringstream,
operator<< resolves to the member function with argument void*, hence
printing out the pointer value.

I believe I understand what everyone is saying, and why this is the
behavior specified by the standard.

But there are still some details I don't understand...

> ...
> IIRC,
>
> * std::string s = (std:stringstream() << std::flush << "abc").string();
>
> behaves as the OP expected, at least on MSVC8 and gcc4.0.1 under XCode
> 3.1.2.


As Jeff says, adding std::flush causes the code to work as I had
expected, namely by printing out "abc".

My variant of the code is:

std::string s =
dynamic_cast<std::stringstream&>(std::stringstream () << std::flush <<
"abc").str();

Note, inserting other stuff in place of std::flush has the same
effect:

std::string s =
dynamic_cast<std::stringstream&>(std::stringstream () << std::skipws <<
"abc").str();

prints out "abc".

std::string s =
dynamic_cast<std::stringstream&>(std::stringstream () << 123 <<
"abc").str();

prints out "123abc".

std::string s =
dynamic_cast<std::stringstream&>(std::stringstream () << "xyz" <<
"abc").str();

prints out <pointer value>"abc".

(I tested these specifically with mingw g++ 4.4.1.)

I'm not sure exactly how the manipulators are processed, but as I
understand it, when first inserting 123 or "xyz", the member-function
operator<< is called, and then returns (*this). (*this) is still
the unnamed temporary, so the second insertion (<< "abc") should
again resolve to the member-function operator<< for void*, and so
should again print out the pointer value rather than "abc".

It's as if the first insertion operator causes the fact that we're
processing an unnamed temporary to be forgotten, and the second
insertion now resolves (incorrectly?) to the free-function operator<<
for char*, and prints out "abc" rather than the pointer value.

Is this now a compiler error, or is there another layer of explanation
that makes this the correct behavior?

>
> Jeff


Thanks Jeff for pointing out the std::flush variation, and thanks to
all
for any further insight.


K. Frank
 
Reply With Quote
 
Victor Bazarov
Guest
Posts: n/a
 
      03-24-2011
On 3/24/2011 4:18 PM, K. Frank wrote:
> Hello Group!
>
> First off, thanks to all who responded.
>
> On Mar 24, 3:13 pm, Jeff Flinn<(E-Mail Removed)> wrote:
>> Victor Bazarov wrote:
>>> On 3/24/2011 12:08 PM, K. Frank wrote:
>>>> The basic question is whether the following line of code
>>>> is legal and good (according to the current standard):

>>
>>>> std::string s1 =
>>>> dynamic_cast<std::stringstream&>(std::stringstream ()<< "abc").str();

>>
>>>> The problem is that "abc" gets rendered as a hex pointer
>>>> value, rather than as "abc".

>> ...
>>> The only function possible is the member function operator<< with the
>>> argument that is void*. The non-member operator<<(stream&, const char*)
>>> cannot be used because there is no binding of a temporary to a reference
>>> to non-const.

>>
>>> The behavior is as expected.

>
> Yes, this explanation seems to be the consensus. As I understand it,
> the code is perfectly legal, but, because of the temporary
> stringstream,
> operator<< resolves to the member function with argument void*, hence
> printing out the pointer value.
>
> I believe I understand what everyone is saying, and why this is the
> behavior specified by the standard.
>
> But there are still some details I don't understand...
>
>> ...
>> IIRC,
>>
>> std::string s = (std:stringstream()<< std::flush<< "abc").string();
>>
>> behaves as the OP expected, at least on MSVC8 and gcc4.0.1 under XCode
>> 3.1.2.

>
> As Jeff says, adding std::flush causes the code to work as I had
> expected, namely by printing out "abc".
>
> My variant of the code is:
>
> std::string s =
> dynamic_cast<std::stringstream&>(std::stringstream ()<< std::flush<<
> "abc").str();
>
> Note, inserting other stuff in place of std::flush has the same
> effect:
>
> std::string s =
> dynamic_cast<std::stringstream&>(std::stringstream ()<< std::skipws<<
> "abc").str();
>
> prints out "abc".
>
> std::string s =
> dynamic_cast<std::stringstream&>(std::stringstream ()<< 123<<
> "abc").str();
>
> prints out "123abc".
>
> std::string s =
> dynamic_cast<std::stringstream&>(std::stringstream ()<< "xyz"<<
> "abc").str();
>
> prints out<pointer value>"abc".
>
> (I tested these specifically with mingw g++ 4.4.1.)
>
> I'm not sure exactly how the manipulators are processed, but as I
> understand it, when first inserting 123 or "xyz", the member-function
> operator<< is called, and then returns (*this). (*this) is still
> the unnamed temporary, so the second insertion (<< "abc") should
> again resolve to the member-function operator<< for void*, and so
> should again print out the pointer value rather than "abc".


No. Unnamed temporary it is, but the call to the first member (to
output the number or the pointer) returns a reference to non-const,
which then is passed directly to the non-member. There is no binding of
a reference to a temporary after the first call.

Example:

struct A {
A& foo();
};

A& bar(A&);

int main() {
A().foo(); // OK
bar(A().foo()); // OK
bar(A()); // not OK - attempt to bind a non-const ref to a temp
}

> It's as if the first insertion operator causes the fact that we're
> processing an unnamed temporary to be forgotten, and the second
> insertion now resolves (incorrectly?) to the free-function operator<<
> for char*, and prints out "abc" rather than the pointer value.


No. The temporary is an object. It's temporary, but not constant. The
language rules require that when a reference is initialized from a
temporary, the reference has to be to a const object. But since the
temporary object is non-const, a non-const member function is allowed to
be called for it. That function can return a reference to non-const,
and you can initialize another non-const ref with that ref, and so on,
and use the object (and change it) *as long as* the temporary is still
*alive*.

> Is this now a compiler error, or is there another layer of explanation
> that makes this the correct behavior?


That's correct behaviour. You can call it a loophole in the language.

>
>>
>> Jeff

>
> Thanks Jeff for pointing out the std::flush variation, and thanks to
> all
> for any further insight.
>
>
> K. Frank


V
--
I do not respond to top-posted replies, please don't ask
 
Reply With Quote
 
K. Frank
Guest
Posts: n/a
 
      03-24-2011
Hi Victor!

And thank you.

On Mar 24, 4:28*pm, Victor Bazarov <(E-Mail Removed)> wrote:
> On 3/24/2011 4:18 PM, K. Frank wrote:
> ...
> >> Victor Bazarov wrote:
> >>> On 3/24/2011 12:08 PM, K. Frank wrote:
> >>>> The basic question is whether the following line of code
> >>>> is legal and good (according to the current standard):

>
> >>>> std::string s1 = dynamic_cast<std::stringstream&>(std::stringstream () << "abc").str();

> ...
> > Note, inserting other stuff in place of std::flush has the same
> > effect:

> ...
> > std::string s = dynamic_cast<std::stringstream&>(std::stringstream () << 123 << "abc").str();
> > prints out "123abc".

> ...
> > I'm not sure exactly how the manipulators are processed, but as I
> > understand it, when first inserting 123 or "xyz", the member-function
> > operator<< *is called, and then returns (*this). *(*this) is still
> > the unnamed temporary, so the second insertion (<< *"abc") should
> > again resolve to the member-function operator<< *for void*, and so
> > should again print out the pointer value rather than "abc".

>
> No. *Unnamed temporary it is, but the call to the first member (to
> output the number or the pointer) returns a reference to non-const,
> which then is passed directly to the non-member. *There is no binding of
> a reference to a temporary after the first call.
>
> Example:
>
> * * *struct A {
> * * * * A& foo();
> * * *};
>
> * * *A& bar(A&);
>
> * * *int main() {
> * * * * A().foo(); // OK
> * * * * bar(A().foo()); // OK
> * * * * bar(A()); // not OK - attempt to bind a non-const ref to a temp
> * * *}
>
> > It's as if the first insertion operator causes the fact that we're
> > processing an unnamed temporary to be forgotten, and the second
> > insertion now resolves (incorrectly?) to the free-function operator<<
> > for char*, and prints out "abc" rather than the pointer value.

>
> No. *The temporary is an object. *It's temporary, but not constant. *The
> language rules require that when a reference is initialized from a
> temporary, the reference has to be to a const object. *But since the
> temporary object is non-const, a non-const member function is allowed to
> be called for it. *That function can return a reference to non-const,
> and you can initialize another non-const ref with that ref, and so on,
> and use the object (and change it) *as long as* the temporary is still
> *alive*.
>
> > Is this now a compiler error, or is there another layer of explanation
> > that makes this the correct behavior?

>
> That's correct behaviour. *You can call it a loophole in the language.


Thank you, Victor, for the clear explanation.

(I will admit that I've now taken a stroll through some alleys and
byways
I hadn't intended to tour.)

I have set myself a homework problem:


<< loophole.cpp >>

#include <iostream>

struct Loophole {
Loophole (int i) : i_(i) {}
Loophole &loophole() { return *this; }
int i_;
};

void print_incr_i (Loophole &l) {
l.i_++;
std::cout << "l.i_ = " << l.i_ << std::endl;
}

int main (int argc, char *argv[]) {
print_incr_i (Loophole (99).loophole());
// next line gives: error: invalid initialization of non-const
reference
print_incr_i (Loophole (66));
}


Does this correctly illustrate your explanation? As it stands, the
program fails to compile, with the error indicated in the comment.
But after commenting out the second call to print_incr_i, the program
compiles and prints out 100, as I would expect.

Even though legal, is this code "bad"? It does seem like I'm
sneaking around a restriction that the language purposely imposes.

And if this is bad, is using the temporary stringstream (with the
"<< std::flush" hack) also bad for the same reason?

> V


Thanks again.


K. Frank
 
Reply With Quote
 
Victor Bazarov
Guest
Posts: n/a
 
      03-24-2011
On 3/24/2011 5:50 PM, K. Frank wrote:
> [..]
> (I will admit that I've now taken a stroll through some alleys and
> byways
> I hadn't intended to tour.)
>
> I have set myself a homework problem:
>
>
> << loophole.cpp>>
>
> #include<iostream>
>
> struct Loophole {
> Loophole (int i) : i_(i) {}
> Loophole&loophole() { return *this; }
> int i_;
> };
>
> void print_incr_i (Loophole&l) {
> l.i_++;
> std::cout<< "l.i_ = "<< l.i_<< std::endl;
> }
>
> int main (int argc, char *argv[]) {
> print_incr_i (Loophole (99).loophole());
> // next line gives: error: invalid initialization of non-const
> reference
> print_incr_i (Loophole (66));
> }
>
>
> Does this correctly illustrate your explanation? As it stands, the
> program fails to compile, with the error indicated in the comment.


Yes, that's the expanded (and functional) version of what my 'foo' and
'bar' intended to show.

> But after commenting out the second call to print_incr_i, the program
> compiles and prints out 100, as I would expect.
>
> Even though legal, is this code "bad"? It does seem like I'm
> sneaking around a restriction that the language purposely imposes.


I don't think the code is bad. The connotation of a "loophole" is kind
of negative, but there is nothing seriously negative with this. Of
course, it is possible to write bad code with it. For instance,

int main() {
Loophole& hole = Loophole(99).loophole();
print_incr_i(hole);
}

What's happening here? Simple: undefined behaviour. The object created
temporarily (the result of the 'Loophole(99)' expression) only lives
until the semicolon that closes the declaration statement. The 'hole'
reference is only valid while it's being initialized. Right after
initialization it becomes invalid, and passing an invalid reference to
the 'print_incr_i' function (and using it inside) causes undefined
behaviour. A seasoned programmer will likely avoid this situation.
However, there can be another, more complicated case, more difficult to
recognise as dangerous. Consider:

#include <loophole.h> // Loophole and 'print_incr_i'

class Doughnut {
Loophole& myhole;
public:
Doughnut(Loophole& hole) : myhole(hole) {}
void roll() { print_incr_i(myhole); }
};

int main() {
Doughnut(Loophole(99).loophole()).roll(); // all is fine
}

In the example above the temporary 'Loophole' survives until 'roll()'
returns, which is OK. Now, somebody decides to hold onto the doughnut
and roll it as many times as they need:

int main() {
Doughnut creamy(Loophole(99).loophole());
for (int i = 0; i < 100; ++i)
creamy.roll();
}

As you imagine, it's the same problem as before. The reference held
inside the Doughnut object becomes invalid as soon as the object is done
initialising. Using that reference inside 'roll' has undefined behaviour.

Often, it's even more complex. Doughnuts would be created in the free
store in one function, where it's unknown the the Loophole is a
temporary, and used in another function. Consider:

void bake(Loophole& hole, Doughnut*& pTastyTreat) {
pTastyTreat = new Doughnut(hole);
}

void consume(Doughnut* pRoundThing) {
if (pRoundThing)
pRoundThing->roll();
}

int main() {
Loophole good_hole(42);
Doughnut *pTreat;
bake(good_hole, pTreat);
consume(pTreat); // all is good
delete pTreat;

bake(Loophole(666).loophole(), pTreat);
consume(pTreat); // KABOOM!!!
delete pTreat;
}

So, there are generally grades of "bad". If the code can potentially
create a maintenance problem, it's better avoided.

> And if this is bad, is using the temporary stringstream (with the
> "<< std::flush" hack) also bad for the same reason?


I think it's better than poking yourself in the eye with a sharp stick
(like one of my old friends liked to say). And it is important to
remember that every "loophole" has its edges which are better not crossed.

V
--
I do not respond to top-posted replies, please don't ask
 
Reply With Quote
 
LR
Guest
Posts: n/a
 
      03-25-2011
Victor Bazarov wrote:
> On 3/24/2011 3:13 PM, Jeff Flinn wrote:
>> Victor Bazarov wrote:
>>> On 3/24/2011 12:08 PM, K. Frank wrote:


>> IIRC,
>>
>> std::string s = (std:stringstream() << std::flush << "abc").string();
>>
>> behaves as the OP expected, at least on MSVC8 and gcc4.0.1 under XCode
>> 3.1.2.

>
> You meant .str(), not .string(), I am sure, and yes, it does.
> Outputting 'std::flush' (which is a pointer to function) uses a member
> op<<, which returns a ref to a non-const stream which then can be passed
> to a non-member op<< to output the characters.



Is this certain?


I've tried
----------------------------------------------------------------------
#include <sstream>
#include <string>

int main() {
const std::string s
= (std:stringstream() << std::flush << "abc").str();
}
----------------------------------------------------------------------

Or very similar, with VS2008, GCC 4.5.2 and
http://www.comeaucomputing.com/tryitout/ see below for version, and all
three produced error messages similar to,


-----------------------------------------------------------------------
Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing. All rights reserved.
MODE:strict errors C++ C++0x_extensions

"ComeauTest.c", line 5: error: class
"std::basic_ostream<char, std::char_traits<char>>" has no member
"str"
const std::string s = (std:stringstream() << std::flush << "abc").str();
-----------------------------------------------------------------------

LR

 
Reply With Quote
 
Victor Bazarov
Guest
Posts: n/a
 
      03-25-2011
On 3/24/2011 8:34 PM, LR wrote:
> Victor Bazarov wrote:
>> On 3/24/2011 3:13 PM, Jeff Flinn wrote:
>>> Victor Bazarov wrote:
>>>> On 3/24/2011 12:08 PM, K. Frank wrote:

>
>>> IIRC,
>>>
>>> std::string s = (std:stringstream()<< std::flush<< "abc").string();
>>>
>>> behaves as the OP expected, at least on MSVC8 and gcc4.0.1 under XCode
>>> 3.1.2.

>>
>> You meant .str(), not .string(), I am sure, and yes, it does.
>> Outputting 'std::flush' (which is a pointer to function) uses a member
>> op<<, which returns a ref to a non-const stream which then can be passed
>> to a non-member op<< to output the characters.

>
>
> Is this certain?


Probably not :-/ My memory must not be as good as it used to be...

>
>
> I've tried
> ----------------------------------------------------------------------
> #include<sstream>
> #include<string>
>
> int main() {
> const std::string s
> = (std:stringstream()<< std::flush<< "abc").str();
> }
> ----------------------------------------------------------------------
>
> Or very similar, with VS2008, GCC 4.5.2 and
> http://www.comeaucomputing.com/tryitout/ see below for version, and all
> three produced error messages similar to,
>
>
> -----------------------------------------------------------------------
> Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for ONLINE_EVALUATION_BETA2
> Copyright 1988-2008 Comeau Computing. All rights reserved.
> MODE:strict errors C++ C++0x_extensions
>
> "ComeauTest.c", line 5: error: class
> "std::basic_ostream<char, std::char_traits<char>>" has no member
> "str"
> const std::string s = (std:stringstream()<< std::flush<< "abc").str();
> -----------------------------------------------------------------------
>
> LR
>



--
I do not respond to top-posted replies, please don't ask
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
integer >= 1 == True and integer.0 == False is bad, bad, bad!!! rantingrick Python 44 07-13-2010 06:33 PM
Bad media, bad files or bad Nero? John Computer Information 23 01-08-2008 09:17 PM
ActiveX apologetic Larry Seltzer... "Sun paid for malicious ActiveX code, and Firefox is bad, bad bad baad. please use ActiveX, it's secure and nice!" (ok, the last part is irony on my part) fernando.cassia@gmail.com Java 0 04-16-2005 10:05 PM
24 Season 3 Bad Bad Bad (Spoiler) nospam@nospam.com DVD Video 12 02-23-2005 03:28 AM
24 Season 3 Bad Bad Bad (Spoiler) nospam@nospam.com DVD Video 0 02-19-2005 01:10 AM



Advertisments