Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does ostream_iterator not work as expected?

Tags:

c++

stream

Needless to say any more than the following code:

#include <utility>
#include <vector>
#include <iostream>
#include <iterator>

using namespace std;

typedef pair<char, char> PAIR;

ostream& operator <<(ostream& os, const PAIR& r)
{
    return os << r.first;
}

int main() 
{
    vector<PAIR> coll; 

    cout << coll[0]; // OK. 

    // The following line will cause a compilation error! Why???
    copy(coll.begin(), coll.end(), ostream_iterator<PAIR>(cout)); 
}
like image 308
xmllmx Avatar asked Dec 15 '10 07:12

xmllmx


2 Answers

The problem is that the name lookup does not find your operator<<(ostream& os, const PAIR& r). The code that tries to invoke the operator<< is in somewhere inside the ostream_iterator<> which is itself inside the std namespace. The name lookup looks around for the right function inside ostream_iterator<> and the std namespace; the argument dependent lookup does not help here because both of the parameters are in the std namespace, too.

So, my suggestion is (1) either to wrap your operator into namespace std { }, but that is UB, IIRC. Or (2) create a struct inheriting from std::pair to define a new type in your namespace, and using the ADL to find your operator<<().

UPDATE:

My 3rd suggestion is to use a custom manipulator to print out the pair.

As for my 2nd suggestion, if you can use C++11, inheriting from std::pair should be easy (untested):

struct PAIR : std::pair
{
  using std::pair::pair;
};

If you cannot use C++11, then I suggest using a custom manipulator.

like image 117
wilx Avatar answered Oct 25 '22 00:10

wilx


This is a common problem : in a word, your operator<< is not seen when instantiating std::ostream_iterator.

During instantiation, name lookup attempts to find an operator<< in the namespace std. Candidates will be found, so no other namespaces will be considered (and, particularly, not the global namespace). Then, overload resolution comes into play : none of the overload matches the argument type, so compilation fails. Note that argument dependent lookup is not of any help here as std::pair also is in namespace std.

You have two solutions :

  • Enclose your operator<< in namespace std { }, although you should know that this is illegal according to the standard (17.4.3.1)
  • Avoid std::copy for this task and use std::for_each (either with an 'old-fashioned' functor or lambda)
like image 21
icecrime Avatar answered Oct 25 '22 01:10

icecrime