12

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)); 
}
xmllmx
  • 39,765
  • 26
  • 162
  • 323

2 Answers2

12

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.

wilx
  • 17,697
  • 6
  • 59
  • 114
10

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)
icecrime
  • 74,451
  • 13
  • 99
  • 111
  • @icecrime, Is this a defect of the C++ standard? Or is there any rationale for that? – xmllmx Dec 15 '10 at 08:12
  • @xmllmx: that's just the way namespaces work, I don't think it's a defect – icecrime Dec 15 '10 at 08:13
  • 1
    @xmllmx: because argument dependent lookup occurs at the point of template instantiation (which in the problematic case is in the namespace `std`) – icecrime Dec 15 '10 at 08:22
  • @icecrime, thank you very much for your quick answer and patient explanations. – xmllmx Dec 15 '10 at 08:31