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

Я определил 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 внутри него, но вызов от первого к последнему, гдепроблема лежит.)

Ответы [ 2 ]

0 голосов
/ 22 февраля 2019

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

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

0 голосов
/ 22 февраля 2019

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

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

DEMO здесь.

Поиск оператора << происходит только в пространстве имен container.value определенin. Похожие сообщения .

...