2

I've done some reseach like:

Tell me if I misunderstood some.

I was trying to input std::pair from a file and I wanted to use std::istream_iterator >

Here comes the code:

std::ifstream in("file.in");

std::map<int, int> pp; 
pp.insert((std::istream_iterator<std::pair<int, int> >(in)),
        std::istream_iterator<std::pair<int, int> >());

I did the overloading as following:

std::istream & operator>>(std::istream & in, std::pair<int, int> & pa)
{ in >> pa.first >> pa.second; return in; }

std::istream & operator>>(std::istream && in, std::pair<int, int> & pa)
{ in >> pa.first >> pa.second; return in; }

Here's the Error:

In file included from /usr/include/c++/4.8/iterator:66:0,
                 from No_12.cpp:7:
/usr/include/c++/4.8/bits/stream_iterator.h: In instantiation of ‘void std::istream_iterator<_Tp, _CharT, _Traits, _Dist>::_M_read() [with _Tp = std::pair<int, int>; _CharT = char; _Traits = std::char_traits<char>; _Dist = long int]’:
/usr/include/c++/4.8/bits/stream_iterator.h:70:17:   required from ‘std::istream_iterator<_Tp, _CharT, _Traits, _Dist>::istream_iterator(std::istream_iterator<_Tp, _CharT, _Traits, _Dist>::istream_type&) [with _Tp = std::pair<int, int>; _CharT = char; _Traits = std::char_traits<char>; _Dist = long int; std::istream_iterator<_Tp, _CharT, _Traits, _Dist>::istream_type = std::basic_istream<char>]’
No_12.cpp:23:59:   required from here
/usr/include/c++/4.8/bits/stream_iterator.h:121:17: error: cannot bind ‘std::istream_iterator<std::pair<int, int> >::istream_type {aka std::basic_istream<char>}’ lvalue to ‘std::basic_istream<char>&&’
      *_M_stream >> _M_value;
                 ^   
In file included from /usr/include/c++/4.8/iostream:40:0,
                 from /files/Nutstore/000/CPPWorkSpace/SHUCourse/DataStructures/BinarySearchTree/20130514/src/BinarySearchTree.hpp:6,
                 from /files/Nutstore/000/CPPWorkSpace/SHUCourse/DataStructures/BinarySearchTree/20130514/BinarySearchTree:3,
                 from No_12.cpp:1:
/usr/include/c++/4.8/istream:872:5: error:   initializing argument 1 of ‘std::basic_istream<_CharT, _Traits>& std::operator>>(std::basic_istream<_CharT, _Traits>&&, _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = std::pair<int, int>]’
     operator>>(basic_istream<_CharT, _Traits>&& __is, _Tp& __x)

I don't know what I can do now... Hope that someone can give me some advice. Thanks.

Community
  • 1
  • 1
Adam
  • 1,684
  • 1
  • 19
  • 39
  • Moreover, I'm using g++-4.8 – Adam May 14 '13 at 13:47
  • It looks to me like your operators are not found (name resolution). I'll try to get some more info and post an answer. – dyp May 14 '13 at 14:00
  • Why the second overload? It looks to me like that is your problem. The normal idiom is to only provide the first overload. – James Kanze May 14 '13 at 14:06
  • 1
    @JamesKanze Doesn't solve the problem; in fact, putting them in the namespace std in clang 3.2 does solve the problem - though I don't know why. – dyp May 14 '13 at 14:10
  • It would make sense if there was a dependent name involved, I'm still trying to figure out where. – dyp May 14 '13 at 14:43
  • @DyP You can't put them in namespace std. (Legally, at least.) And of course, the problem is that the only elements outside of `std` are `int`, so the global namespace isn't drawn into ADL. Just use a wrapper, as you suggest in your answer. – James Kanze May 14 '13 at 14:44
  • But I'd still like to know why he thinks it necessary to add the overload for the rvalue reference. – James Kanze May 14 '13 at 14:44
  • @Dyp Why did you delete that post T.T ... I'm about to read it... – Adam May 14 '13 at 14:45
  • @Adam Have undeleted it. Was right :) Backed it up. – dyp May 14 '13 at 14:50
  • One advice would be to use clang++ as its error messages are far more legible ;) – dyp May 14 '13 at 14:59
  • Another advice: using brace-init-lists avoids most-vexing parse: `pp.insert( std::istream_iterator>{in}, std::istream_iterator>{});` The double `>>` is also correctly parsed in C++11. – dyp May 14 '13 at 15:08
  • Interestingly, this is [actually a duplicate](http://stackoverflow.com/q/4447827/420683) – dyp May 14 '13 at 15:36

1 Answers1

2

To understand what's the problem, we have to take a look at an exemplary implementation of istream_iterator (the problem arises from the requirements of the Standard, which are implicitly shown here):

template < class T, class CharT, class Traits, class Distance >
class istream_iterator
{
    /* ... */
    typedef std::basic_istream<CharT, Traits>  istream_type;
    istream_type* stored_istream_ptr;
    T value;

public:
    /* ... */
    istream_iterator& operator++()
    {
        *stored_istream_ptr >> value; // (A)
        return *this;
    }
    T const& operator*() const
    {
        return value;
    }
    /* ... */
};

In line (A), an operator is applied to objects of dependent types:

  • value is of type T
  • stored_istream_ptr is of type istream_type, see the typedef

According to [temp.dep.type]/8, both types are dependent.

For the expression A >> B as well as for any other function call, first the name of the function is looked up (here: operator>>) -> name lookup, then, from the set of found functions (overloads), the most viable is chosen and called -> overload resolution.

For an operator, both member and non-member functions (such as your operators) are looked up.

In this case, the involved types are dependent, therefore special rules for name lookup apply [temp.dep.res]/1:

In resolving dependent names, names from the following sources are considered:

  • Declarations that are visible at the point of definition of the template.
  • Declarations from namespaces associated with the types of the function arguments both from the instantiation context (14.6.4.1) and from the definition context.

Your operators have been defined in the global namespace, which is not associated to neither std::basic_istream nor std::pair. Therefore, name resolution does not find your operators and overload resolution for the expression in line (A) fails.


This would explain clang 3.2's error message:

stream_iterator.h:120:17: error: invalid operands to binary expression ('istream_type' (aka 'basic_istream < char, std::char_traits >') and 'std::pair')

       *_M_stream >> _M_value;
       ~~~~~~~~~~ ^  ~~~~~~~~

And it explains why the workarounds work.

g++ on the other hand seems to only show one overload found by name resolution and the reason it's rejecting it (whereas clang++ shows all overloads found by name resolution with a reason for every single one why it has been rejected). The one g++ shows might be the "best fitting":

template< class CharT, class Traits, class T >
basic_istream<CharT,Traits>& operator>>(basic_istream<CharT,Traits>&&, T&);

Which is, AFAIK, just a wrapper to call the other operator>> in the case the expression istream_obj >> value operates on a rvalue (like get_istream() >> value).

This is unrelated to your problem (and confusing).


Two workarounds:

  • use a wrapper type to wrap std::pair such that you can define the operator>> in a namespace associated with that wrapper type
  • inject your operators in namespace std (not recommended??)
dyp
  • 38,334
  • 13
  • 112
  • 177
  • With regards to the wrapper class: this might be a legitimate case to derive from `std::pair`. Otherwise, a `operator std::pair() const` will do the trick. (I do this a lot, where the type I'm iterating over is actually something like `struct { char const* key, int value; operator std::pair() const; };`, which can be statically initialized.) – James Kanze May 14 '13 at 14:47
  • Woo... This is awesome!!! but ... one tiny problem ... If I'd use a wrapper class, I'll have to change all other stuffs in my code to fullfill this... – Adam May 14 '13 at 15:22
  • @Adam I found a workaround, see second part of [this question](http://stackoverflow.com/q/16548379/420683) – dyp May 14 '13 at 17:06