Краткий ответ: Это ошибка компилятора в MSVC 2015 и старше; Чтобы обойти это, напишите using ::operator<<;
непосредственно перед строкой с сообщением об ошибке.
Эта проблема касается поиска имен в определениях встроенных друзей.
Вот упрощенный пример проблемы:
namespace R { struct S {}; }
void f(R::S) {}
namespace N
{
struct E
{
R::S var;
friend void f(E e) { f(e.var); } // OK, should find f(R::S)
};
}
Как правило, поиск по неквалифицированному имени будет делать две вещи:
- Ищите имя в текущей области. Если не найдено, посмотрите в родительской области и т. Д., Включая глобальное пространство имен. Остановитесь, когда мы найдем имя . То есть, если имя находится в текущей области, а также в родительской области, тогда имя в родительской области не будет найдено.
- ADL, т. Е. Также поиск в пространствах имен любых аргументов вызова функции.
Обратите внимание, что в этом коде f(R::S)
не объявлено в пространстве имен R
, поэтому ADL никогда не найдет его. Его можно найти только по первой части безошибочного поиска.
Таким образом, существует потенциальная проблема, заключающаяся в том, что любое имя f
, встречающееся внутри namespace N
, может скрывать глобальное f
. Это можно увидеть в действии, если вы удалите строку friend
и поместите void f(E e) { f(e.var); }
как функцию в N
(не в E
). Тогда поиск имени находит N::f
и прекращает поиск, не найдя ::f
.
Теперь, поиск имени friend
функции , впервые определенной внутри класса, немного необычен. Цитирование из cppreference :
Имя, впервые объявленное в объявлении друга в классе или шаблоне класса X, становится членом внутреннего вложенного пространства имен X, но не отображается для поиска (кроме поиска, зависящего от аргумента, который учитывает X), если только не найдено соответствующее объявление в предоставляется пространство имен.
Это означает, что при вызове f(e.var)
функция N::f
фактически не видна для поиска. Таким образом, поиск должен продолжаться до тех пор, пока мы не найдем ::f
, и добьемся успеха.
MSVC 2015, похоже, знает об этом правиле поиска друзей, поскольку оно корректно отклоняет struct A { friend void a() { a(); } };
, однако затем не удается продолжить поиск внешних областей для другого объявления имени.
Объявление using ::operator<<;
означает, что при поиске N
будет найдено ::operator<<
; видя, что MSVC 2015 принимает это, по-видимому, он все еще ищет N
, но просто не повторяется вверх, если поиск не удался.
Комментарий: Эта проблема с тенями имен функций всегда является проблемой, когда у вас есть operator<<
, который не найден ADL. Даже в правильном компиляторе вы можете обнаружить, что ваш код раздражающе перестает работать, когда у вас есть какие-то несвязанные operator<<
помехи. Например, если вы определите operator<<
в namespace Logging
, то даже в g ++ и MSVC 2017 код перестанет работать.
В этом случае работает тот же using ::operator<<
обходной путь, но раздражает необходимость продолжать это делать. Вы действительно должны сделать using ::operator<<
внутри любого пространства имен N
, которое объявляет свои собственные operator<<
любого вида, что все еще раздражает, но немного меньше. Для этой цели может быть предпочтительнее использовать функцию с несколько уникальным именем вместо operator<<
.
Обратите внимание, что ADL не может найти operator<<(std::ostream, std::chrono...)
, поскольку все аргументы находятся в std
, а ::operator<<
- в std
. Неопределенное поведение - добавлять свои собственные бесплатные функции в namespace std
, поэтому вы не можете обойти это и так.