3

Я пытаюсь использовать stl copy() для печати пары ключ-значение на карте. Код выглядит следующим образом:Ошибка компиляции при перегрузке оператора << с помощью аргументов шаблона

#include <iterator> 
#include <iostream> 
#include <algorithm> 
#include <map> 

using namespace std; 
//compile error if I comment out "namespace std" 
namespace std {  
template<typename F, typename S> 
ostream& operator<<(ostream& os, const pair<F,S>& p) { 
    return os << p.first << "\t" << p.second << endl; 
} 
} 

int main() { 
    map<int, int> m; 
    fill_n(inserter(m, m.begin()), 10, make_pair(90,120)); 
    copy(m.begin(), m.end(), ostream_iterator<pair<int,int> >(cout,"\n")); 
} 

Я пытаюсь перегрузить оператор < <. Проблема в том, что код не будет компилироваться, если я не буду окружать определение перегруженного оператора < < с namespace std. Я думаю, что это связано с механизмом поиска имен на C++, который у меня все еще есть проблема с пониманием. Даже если я определяю версию без шаблонов:

Он по-прежнему не компилируется. Может ли кто-нибудь объяснить, почему?

+0

[Пример ошибки] (http://liveworkspace.org/code/3MJn1M$1) –

ответ

2

Ваша проблема с argument-dependent name lookup (ADL). Компилятор ищет реализацию operator<< в namespace std, так как и ostream и pair находятся в этом пространстве имен. Вы должны сделать обертку, которая перенаправляет на operator<< от правильного пространства имен:

template<class T> 
struct ostreamer { 
    ostreamer(const T& value) : reference(value) {} 
    const T& reference; 
    friend ostream& operator<<(ostream& stream, const ostreamer& value) { 
    return stream << value.reference; 
    } 
}; 

Тогда просто использовать ostream_iterator<ostreamer<pair<const int, int>>> вместо ostream_iterator<pair<int, int>>. Обратите внимание, что поскольку ostreamer хранится по ссылке, а не по значению, вы не можете больше полагаться на неявное преобразование от pair<const int, int> до pair<int, int>. Вы можете изменить ostreamer для хранения по значению, но, поскольку это так, у него нет накладных расходов, и я думаю, что в любом случае лучше быть явным.

+0

На самом деле безопасно перегружать материал в пространстве имен std, если оно связано с определенным пользователем типом (я попытаюсь найти ссылка на спецификацию позже). Поскольку это не означает ни OP, ни ваш пример. – John5342

+0

@Jon Спасибо за ваш ответ! Означает ли это, что компилятор ТОЛЬКО ищет функцию в пространстве имен std и больше нигде? Как насчет других мест, таких как текущая единица перевода? – rialmat

+0

@ John5342 Я хотел бы знать обоснование: «На самом деле безопасно перегружать материал в пространстве имен std, если оно связано с определенным пользователем типом». Я думаю, что конструкция 'pair' здесь похожа на пользовательский тип, оператор << не знает, как вывести пару, точно так же, как он не знает, как выводить определенный пользователем тип. – rialmat