Я определил 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
нет в списке.
(Это сообщение от GCC Debian 6.3.0 с -std=c++14
. Я получаю ту же ошибку, с другой формулировкой, от Clang Debian 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
, и я не могу это изменить.
Мне не понятно почему my global 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<<
. Boost предоставляет один для optional
, и я написал один для std::pair
внутри него, но вызов от первого к последнему, гдепроблема лежит.)