Пространства имен и разрешение оператора - PullRequest
36 голосов
/ 04 марта 2011

Я использую библиотеку, которая определяет операторы потока вывода (operator <<) в глобальном пространстве имен.В своем собственном пространстве имен я всегда объявлял такие операторы в глобальном пространстве имен и никогда не имел проблем с ними.Но теперь по разным причинам мне нужно объявить эти операторы в моем собственном пространстве имен, и внезапно компилятор не может найти операторы, объявленные в библиотеке. </p>

Вот простой пример, иллюстрирующий мою проблему:

#include <iostream>

namespace A
{
   struct MyClass {};
}

std::ostream & operator<<( std::ostream & os, const A::MyClass & )
   { os << "namespace A"; return os; }

namespace B
{
   struct MyClass {};

   std::ostream & operator<<( std::ostream & os, const B::MyClass & )
      { os << "namespace B"; return os; }
}

namespace B
{
   void Test()
   {
      std::cout << A::MyClass() << std::endl;
      std::cout << B::MyClass() << std::endl;
   }
}

int main()
{
   B::Test();
   return 1;
}

Я получаю следующую ошибку:

error: no match for ‘operator<<’ in ‘std::cout << A::MyClass()’

Обратите внимание, что если оба оператора находятся в пространствах имен или, альтернативно, если они оба находятся в глобальном пространстве имен, код компилируется ивыполняется правильно.

Мне бы очень хотелось понять, что происходит, а также, что такое «хорошая практика» для определения таких операторов с пространствами имен.

Спасибо!

Ответы [ 4 ]

37 голосов
/ 04 марта 2011

Поскольку Test находится в пространстве имен B, компиляция видит оператор в этом пространстве имен и отмечает, что у него нет соответствующей подписи.Он также пытается найти оператор в пространстве имен A, которое содержит класс, но также не может найти его там.Поскольку в пространстве имен B уже есть такой оператор (с неверной подписью), он не будет пытаться найти его в глобальной области видимости.

Причина, по которой он не ищет глобальную, - это примерноследующее.Сначала я процитирую стандарт, а затем попытаюсь объяснить его.

Из 3.4 / 1:

... Поиск имени может связать более одного объявления с именемесли он находит имя как имя функции;говорят, что объявления образуют набор перегруженных функций (13.1).Разрешение перегрузки (13.3) происходит после успешного поиска имени.

Когда я читаю это, когда компилятор пытается найти функцию (в этом контексте ваши операторы), он сначала пытается это сделать.поиск имени, чтобы найти функцию в первую очередь.Затем он пытается выбрать правильную функцию из набора перегрузок.

Теперь из 3.4.1 / 6:

Имя, используемое в определении функции (26)который является членом пространства имен N (где только для целей представления N может представлять глобальную область видимости) должен быть объявлен перед его использованием в блоке, в котором он используется, или в одном из его включающих блоков (6.3) или,должен быть объявлен перед его использованием в пространстве имен N или, если N является вложенным пространством имен, должен быть объявлен перед его использованием в одном из вмещающих пространств имен N.

Давайте разберем это.Вы используете operator<< в функции уровня пространства имен, поэтому этот раздел применяется.Он попытается найти этот оператор, используя приоритет в описанном выше.Ваш оператор не объявлен в текущем или вложенных блоках (это относится к вложенному {} в вашей функции).Однако следующая часть соответствует «... должна быть объявлена ​​до ее использования в пространстве имен N ...».Там равно на самом деле operator<< в текущем пространстве имен (B), поэтому он добавляет этот оператор в свой список совпадений.В B больше нет совпадений, и поскольку область одинакового пространства имен считается наилучшей возможной близостью совпадения, она не будет рассматривать другие области.

Причина, по которой это работает, когда вы помещаетеоператор в пространство имен A заключается в том, что поскольку печатаемый элемент является членом A, это пространство имен фактически рассматривается, поскольку оно включено в пространства имен выражения.Поскольку пространство имен A считается , он находит соответствующее совпадение в этом пространстве имен и правильно компилирует.

Теперь, когда у него есть список возможных операторов, он пытается выполнить разрешение перегрузки для них.К сожалению, один из найденных в пространстве имен B является единственным, который он рассматривает, и он не соответствует требуемым аргументам.

В общем случае операторы вставки должны быть в том же пространстве имен, что и класс, с которым он работает.

11 голосов
/ 04 марта 2011

Мой ответ очень похож на другие, но, в частности, компилятор пытается найти A :: operator << (), потому что он работает с чем-то в пространстве имен A. Если вы хотите вызвать тот, который находится вне пространства имен, вы можете вызвать его явно, используя </p>

::operator<<(std::cout, A::MyClass();

Для более плавного синтаксического использования поместите его в пространство имен.

9 голосов
/ 28 сентября 2015

Проблема была объяснена в ответе @Mark B. Следующее решает проблему.В пространстве имен, в котором вы хотите использовать глобальный operator<<, введите следующий код:

using ::operator<<;

В примере кода OP эта строка кода будет идти в расположение вдоль другого кода, который объявляет / определяетoperator<< для namespace B:

namespace B
{
   struct MyClass {};

   std::ostream & operator<<( std::ostream & os, const B::MyClass & )
      { os << "namespace B"; return os; }

   using ::operator<<;
}
0 голосов
/ 04 марта 2011

Это потому, что ваш первый operator<<() определен вне пространства имен A.

...