2015-12-10 8 views
1

У меня есть следующий код:C++ Выходной поток не работает с шаблонами и пространствами имен

#include <fstream> 

// Removing this namespace (keeping the content) makes it work 
namespace baz { 

class Bar { 
}; 

} 

std::ostream & operator<<(std::ostream & stream, baz::Bar & value) { 
    return stream; 
} 

// Removing this namespace (keeping the content) makes it work 
namespace goo { 

template <class Type> 
struct Point { 
}; 

// Removing this function makes it work 
template <class Type> 
std::ostream& operator<< (std::ostream& stream, const Point<Type> &point); 

void foo() { 
    baz::Bar test; 
    std::ofstream stream; 
    stream << test; 
} 

} 

Это не компилируется на MSVC и выдает следующее сообщение об ошибке:

ошибка C2679: бинарный ' < < ': ни один оператор не найден, который принимает с правой операнда типа „Базом :: Bar“ (или нет приемлемого преобразования)

Однако, если я удаляю любое из двух пространств имен (сохраняя все содержимое) или удаляя шаблонную функцию << для класса Point, все работает нормально.

Это ошибка в MSVC? Как я могу скомпилировать его, не удаляя пространства имен или функцию?

+0

Попробуйте использовать 'operator :: operator <<;' или переместите его в 'namespace baz'. –

ответ

3

Это ошибка в коде. Набор перегрузок для функций строится в несколько этапов:

  1. Из текущего объема наружу найдите соответствующее имя функции. Когда имя находится в пространстве имен, добавьте все перегрузки, определенные в этом пространстве имен, в набор перегрузки и остановите этот процесс.
  2. Включите все перегрузки, найденные в зависимости от зависимостей от параметров (ADL), к набору перегрузки.
  3. Определите, содержит ли набор перегрузки уникальное соответствие и наилучшую перегрузку. Если это так, используйте это иначе.

Ваша проблема заключается в том, что ваш operator<<(std::ostream&, baz::Bar&) определяется в глобальном пространстве имен, а не в пространстве имен, где Bar определяется (в данном случае baz). В любом случае использование перегрузки в пространстве имен baz необходимо при использовании оператора из шаблона с именем в зависимости от аргумента шаблона: в этом случае первый этап опущен и обнаруживаются только имена, найденные с помощью зависимого от аргумента поиска.

BTW, фиксируя местоположение оператора вывода, вы можете рассмотреть возможность передачи аргумента как baz::Bar const&, поскольку операторы вывода обычно не изменяют форматированный объект.

+0

Вы говорите, что из текущего объема наружу, так что регулярный поиск останавливается, когда он находит первую функцию? Почему он не находит глобальный? – 0x499602D2

+0

0x499602D2: поиск в текущем контексте прекращается, когда он находит первую функцию с соответствующим именем. В этом примере поиск 'operator <<' останавливается, когда найдено 'goo :: operator <<', и глобальное пространство имен никогда не выполняется (при удалении' goo :: operator << 'глобальное пространство имен _is_ искали). Поскольку аргументы также включают пространство имен 'std' (из первого аргумента) и пространство имен' baz' (из второго аргумента), имена из одного из этих пространств имен добавляются со второй части (ADL). Однако ни одна из добавленных таким образом функций 'operator <<' не является совпадением для 'baz :: Bar'. –