Is this in the defect report?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <string>
#include <sstream>

int f(std::string const &s)
{
	int a, b;
	if(std::istringstream{s} >> a >> b)
	{
		return a + b;
	}
	return 0;
}

int main()
{
	std::cout << f("1 2") << std::endl;
}
This program unintuitively contains undefined behavior (thanks to jared11 for bringing it to my attention).
Where is it?
It's in two places on the same line.
So is it on line 8?
It might have been subtle but I was asking for an explanation.
Last edited on
I'm still double-checking with some compilers, but as far as I can tell, after >> a, the istringstream temporary could be destructed, meaning that >> b and operator bool() could be used on a destructed object.

I'm actually not certain why a non-const reference is binding to a temporary - I could be wrong and there could be a special rule for it, but when I use a non-stream class the code does not compile.
Last edited on
I don't see any undefined behavior here. The temporary created on line 8 is required to be around for the evaluation of the full expression, which includes the calls to operator>>.
cire: so is that a special rule on for the standard stream classes? I cannot replicate it:
http://coliru.stacked-crooked.com/a/c639bae570ea815f
Last edited on
cire: so is that a special rule on for the standard stream classes?


No.

See http://stackoverflow.com/a/4214179/1708463
OK, so there's no UB, but why can the temporary stream object bind to a non-const reference whereas my class cannot?

What threw me off about the UB was this:
http://www.cplusplus.com/forum/general/122370/
Since operator>> is a function, I just assumed the same rules applied. Apparently it is a special case like you said :p
Last edited on
Remember, you're dealing with C++11. There is such a thing as rvalue references and, not surprisingly, you'll find an operator>> overload taking one.
Last edited on
operator>> (int) is a member function; it returns a reference, not a prvalue; no overload is required.

See: http://www.cplusplus.com/forum/beginner/122507/

Also see: http://coliru.stacked-crooked.com/a/bc4af87525057a00

Note: operator>> ( basic_istream<>, basic_string<> ) (which is not a member function) has an overload for rvalue reference to stream.
Last edited on
What threw me off about the UB was this:
http://www.cplusplus.com/forum/general/122370/
Since operator>> is a function, I just assumed the same rules applied.


If you change your program to match what you're linking to, you'd have a dangling reference too:
1
2
std::istream& ss = std::istringstream(s) >> a;  // temporary dies at the end of full expression (the semicolon in this case)
ss >> b; // ss is a dangling reference, UB 


PS: C++11 not necessary
Last edited on
JLBorges wrote:
operator>> (int) is a member function; it returns a reference, not a prvalue; no overload is required.
Ah, this makes a lot more sense now - thanks!
Hm, that does still bother me actually - this means the first thing you input cannot be a user-defined type. That doesn't seem right to me - so I ask, is this new thing I just realized in the defect report?
Hm, that does still bother me actually - this means the first thing you input cannot be a user-defined type. That doesn't seem right to me - so I ask, is this new thing I just realized in the defect report?


Huh? I'm not sure what you're asking.

It sounds like you're saying the following won't work:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <string>
#include <sstream>

struct A
{
    std::string text;
};

std::istream& operator>>(std::istream& is, A& a)
{
    return is >> a.text;
}

int f(std::string const &s)
{
    A a;
    int b;
    if (std::istringstream{ s } >> a >> b)
    {
        return std::stoi(a.text) + b;
    }
    return 0;
}

int main()
{
    std::cout << f("1 2") << std::endl;
}

http://ideone.com/ZHSGYd

But it does, of course. Are you asking something else?
> Hm, that does still bother me actually - this means the first thing you input cannot be a user-defined type.
> That doesn't seem right to me

The standard library has a templated overload which takes rvalue refererence to input stream.
1
2
template < typename CHAR_TYPE, typename TRAITS_TYPE, typename T >
basic_istream<CHAR_TYPE, TRAITS_TYPE>& operator>> ( basic_istream<CHAR_TYPE, TRAITS_TYPE>&&, T& ) ;

Which delegates to the overloaded operator which takes an lvalue reference to input stream:
operator>> ( basic_istream<CHAR_TYPE, TRAITS_TYPE>&, T& ) ;

This snippet may help clarify matters:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <string>
#include <sstream>

struct A { std::string text ; };
struct B { std::string text ; };
struct C { std::string text ; };
struct D { std::string text ; };

std::istream& operator>> ( std::istream& is, A& a ) { return is >> a.text ; }
// the library has a templated overload for operator>> ( istream&&, T& )
// which delegates to the overload for operator>> ( istream&, T& )

B& operator>>( B& b, A& a ) { b.text += '0' ; a.text = b.text ; return b ; }

// non-template overload for operator>> (B&&, A&), which delegates tooperator>> (B&, A&)
B& operator>>( B&& b, A& a ) { return b >> a ; } 

C& operator>>( C& c, A& a ) { c.text += "00" ; a.text = c.text ; return c ; }

// templated overload for operator>> ( C&&, T& ) which delegates to operator>> ( C&, T& )
template < typename T > C& operator>>( C&& c, T& t ){ return c >> t ; }

D& operator>>( D& d, A& a ) { d.text += "000" ; a.text = d.text ; return d ; }
// D has no overload for operator>> ( D&&, A& )

int main()
{
    std::cout << "-----------------------\n" ;

    A x, y ;
    
    // fine, uses templated overload for ( istream&&, T& ) (with T==A)
    std::istringstream {"12 34 "} >> x >> y ; 
    std::cout << x.text << ' ' << y.text << '\n' ;

    B{"56"} >> x >> y ; // fine, uses overload for ( B&&, A& )
    std::cout << x.text << ' ' << y.text << '\n' ;
    
    C{"78"} >> x >> y ; // fine, uses templated overload for ( C&&, T& ) (with T==A)
    std::cout << x.text << ' ' << y.text << '\n' ;
    
    #ifdef USE_D
        D{"90"} >> x >> y ; // ***error: operator>> ( D&, A& ) expects l-value reference to D
        std::cout << x.text << ' ' << y.text << '\n' ;
    #endif // USE_D
    
    std::cout << "------- done ---------\n" ;
}

http://coliru.stacked-crooked.com/a/e174f1a08642baf1
Topic archived. No new replies allowed.