Почему ostream_iterator работает не так, как ожидалось?

Излишне говорить больше, чем следующий код:

#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)); 
}

Ответ 1

Проблема заключается в том, что поиск по имени не находит ваш operator<<(ostream& os, const PAIR& r). Код, который пытается вызвать operator<<, находится где-то внутри ostream_iterator<>, который сам находится внутри пространства имен std. Поиск имени просматривает правильную функцию внутри ostream_iterator<> и пространства имен std; зависимый от аргумента поиск не помогает здесь, потому что оба параметра также находятся в пространстве имен std.

Итак, мое предложение: (1) либо обернуть оператора в namespace std { }, но это UB, IIRC. Или (2) создайте структуру, наследующую от std::pair, чтобы определить новый тип в вашем пространстве имен и используя ADL, чтобы найти ваш operator<<().

UPDATE:

Мое третье предложение - использовать пользовательский манипулятор для печати пары.

Что касается моего второго предложения, если вы можете использовать С++ 11, наследование от std::pair должно быть простым (непроверенным):

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

Если вы не можете использовать С++ 11, я предлагаю использовать настраиваемый манипулятор.

Ответ 2

Это обычная проблема: одним словом, ваш operator<< не отображается при создании экземпляра std::ostream_iterator.

Во время экземпляра поиск имени пытается найти operator<< в пространстве имен std. Кандидаты будут найдены, поэтому никакие другие пространства имен не будут рассмотрены (и, в частности, не глобальное пространство имен). Затем вступает в действие разрешение перегрузки: ни одна из перегрузок не соответствует типу аргумента, поэтому компиляция не выполняется. Обратите внимание, что поиск зависимых от аргументов здесь не помогает, так как std::pair также находится в пространстве имен std.

У вас есть два решения:

  • Включите operator<< в namespace std { }, хотя вы должны знать, что это незаконно в соответствии со стандартом (17.4.3.1)
  • Избегайте std::copy для этой задачи и используйте std::for_each (либо с помощью "старомодного" функтора, либо лямбда)