Почему функция в пространстве имен не видит мой оператор <<, определенный глобально?

Я определил operator<< функция выхода для std::pair экземпляры, для использования некоторыми модульными тестами, которые хотят напечатать значения, если они не смотрят то, что ожидали. Мой тестовый код также содержит пары, которые содержатся как члены другого класса, который имеет свой собственный operator<< - конкретно boost::optional, но для примера я определил простой Container класс здесь вместо Проблема в том, что operator<< за std::pair значения не видны в пределах operator<< контейнерного класса.

#include <iostream>
#include <utility>

template <typename T1, typename T2>
std::ostream &operator<<(std::ostream &out, std::pair<T1, T2> const &pair) {
  return out << "{ " << pair.first << ", " << pair.second << " }";
}

namespace {

  template <typename T>
  struct Container {
    T value;
  };

  template <typename T>
  std::ostream &operator<<(std::ostream &out, Container<T> const &container) {
    return out << container.value;  // Error!
  }

}

int main() {
  std::pair<char, int> pair { 'a', 1 };
  Container<std::pair<char, int>> container { pair };

  std::cout << pair << std::endl;
  std::cout << container << std::endl;
}

Линия в конце, которая выводит простую пару, работает нормально. Но при попытке вывести пару в контейнере компилятор не может найти operator<< для пар. Вот сообщение от GCC:

test.cc: In instantiation of ‘std::ostream& {anonymous}::operator<<(std::ostream&, const {anonymous}::Container<T>&) [with T = std::pair<char, int>; std::ostream = std::basic_ostream<char>]’:
test.cc:28:16:   required from here
test.cc:18:16: error: no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘const std::pair<char, int>’)
     return out << container.value;
            ~~~~^~~~~~~~~~~~~~~~~~

... с последующим длинным списком всех кандидатов operator<< функции, которые были рассмотрены, и почему каждая из них не подходит (потому что они все для разных типов значений). Мой шаблон для std::pair нет в списке

(Это сообщение от Debian GCC 6.3.0 с -std=c++14, Я получаю ту же ошибку с другой формулировкой из Debian Clang 3.8.1-24 с -std=c++14 и Apple Clang 1000.11.45.5 (Apple LLVM 10.0.0) с -std=c++17.)

Если я удалю анонимное пространство имен вокруг моего Container шаблон и его operator<< ошибка уходит. Но это не совсем решение, так как на самом деле контейнер boost::optional что, конечно, находится в пространстве имен boost и я не могу это изменить.

Мне не понятно, почему мой глобальный operator<< не виден из пространства имен, так как глобальная область должна быть частью пути поиска для неквалифицированного поиска. Мое лучшее предположение, что это потому, что мой operator<< является шаблоном, и шаблоны, кажется, не являются частью первоначального неквалифицированного поиска, поэтому ADL запускает и находит кучу других operator<< функции, определенные в std:: и как члены в std::ostream Таким образом, поиск останавливается там. Список функций-кандидатов (в сообщении об ошибке компилятора), похоже, соответствует этой интерпретации. Но тогда неясно, почему он работает, когда контейнер не находится в пространстве имен.

Есть ли способ сделать эту работу без изменения Container учебный класс?


(В качестве фона: я использую библиотеку Boost.Test и пишу такие строки, как BOOST_TEST(some_func() == boost::make_optional(std::make_pair('a', 1))), где BOOST_TEST выполняет магию макросов / шаблонов для извлечения двух сторон выражения и вывода их значений, если они не совпадают. Это требует, чтобы значения имели operator<< определены. Повышение обеспечивает один для optional и я написал один для std::pair внутри него, но проблема в том, где находится проблема.)

2 ответа

Решение

Неквалифицированный поиск поднимается на один уровень за раз и останавливается, как только находит что-то. Находит operator<< внутри анонимного пространства имен - того самого, которому вы звоните, - и тут же останавливается.

Подумайте об упаковке элемента pair или pair себя в обертку в вашем собственном пространстве имен. Затем вы можете определить operator<< делать все, что вы хотите, и ADL забрал его.

Есть ли способ сделать это без изменения класса контейнера?

Да. Вы должны поставить operator<< внутри пространства имен.

ДЕМО здесь.

Поиск оператора << происходит только в пространстве имен container.value определяется в. Связанный пост.

Другие вопросы по тегам