Вопрос

Возможный дубликат:
Где должны быть размещены перегрузки оператора не-члена?

При просмотре так я часто нахожу вопросы или ответ, который включает в себя перегрузку / определение std::ostream& operator<<(std::ostream& os, const Foo& foo) или а Foo operator+(const Foo& l, const Foo& r).

Пока я знаю, как и когда (не) написать эти операторы, я путающуюся о namespace предмет.

Если у меня есть следующий класс:

namespace bar
{
  class Foo {};
}

В котором namespace Должен ли я написать различные определения оператора?

// Should it be this

namespace bar
{
  std::ostream& operator<<(std::ostream& os, const Foo& foo);
}

// Or this ?

namespace std
{
  ostream& operator<<(ostream& os, const bar::Foo& foo);
}

// Or this ?

std::ostream& operator<<(std::ostream& os, const bar::Foo& foo);

Тот же вопрос относится к operator+. Отказ Итак, какова хорошая практика здесь и Зачем ?

Это было полезно?

Решение

Это должно быть в bar пространство имен. Вы должны рассмотреть Что составляет интерфейс для класса, и группируйте их вместе.

«Класс описывает набор данных вместе с функциями, которые работают на этих данных». Ваша бесплатная функция работает на Foo, поэтому это часть Foo. Отказ Это должно быть сгруппировано с Foo в пространстве имен bar.

Зависимый от аргумента, или ADL, найдут функцию.

Мы также знаем, что мы должны Предпочитаю не-друга, не являющиеся членами функций. Отказ Это означает, что в целом ваши классы будут иметь свои определение и функции членов, а затем выполняются бесплатные функции, которые работают на классе.

Другие советы

Правило заключается в том, что при поиске подходящей функциональной перегрузки рассматриваются как текущее пространство имен, так и все пространства имен определений определений типа аргумента. Это называется агистрационным поиском (ADL).

Так что, когда у вас есть этот код:

  ::std::ostream& os = /* something */;
  const ::bar::Foo& foo = /* something */;
  os << foo;

Следующие пространства имен рассматриваются:

  • Текущее пространство имен
  • :: std, потому что тип ОС определяется там
  • :: Бар, потому что тип Foo определен там

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

Однако....

Вам не разрешено определять новые функции в :: std, поэтому вы не можете поставить свой перегруженный оператор в этом пространстве имен. (Вам разрешено специализироваться на шаблонах в :: std, но это не то, что мы здесь делаем)

Во-вторых, «текущее пространство имен» может измениться, поэтому, если вы поместите определение функции в этом пространстве имен, это может не всегда найти.

Итак, в конце концов, лучшее место для поставок перегруженного оператора находится в том же пространстве имен, что и FOO:

namespace bar   
{   
  std::ostream& operator<<(std::ostream& os, const Foo& foo);   
}   

Для перегрузки оператора правильно работать, функция должна быть в том же пространстве имен в качестве одного из его операндов. В противном случае ADL не находит его. Это означает пространство имен вашего класса для операторов, таких как + и -. Теоретически, вы можете поставить оператора << в STD или одно и то же пространство имен, что и ваш класс, но стандартные запрещены определяют новые функции в STD, поэтому здесь также выкладываете его в то же пространство имен в качестве класса.

(И, конечно, вы обычно не реализуете + или -, но + = и - =, а затем вытекаете из шаблона, который обеспечивает + и - автоматически.)

Лучший выбор - это вариант 1. Почему? Поскольку, когда вы используете неквалифицированное имя функции (перегруженный оператор - это функция), помимо нормального поиска имени, применяется, зависимый от аргументов, который находятся (неформально) все пространства имен, где были объявлены аргументы. Например

namespace N
{
   class X(){};
   void f(X){}
}
int main()
{
    N::X x;
    f(x); //works fine, no need to qualify f like N::f
}

То же самое с операторами.

С другой стороны, в случае опции 2 оператор все еще будет найден, потому что OstTream находится в STD (одинаковое правило ADL). Но это не очень хорошая идея, чтобы добавить вещи на STD пространство имен.

И третий вариант плохой, стилистично - зачем это сделать это, если первый вариант достаточно?

Итак, определенно вариант 1.

Хет

Хорошая практика заключается в том, чтобы объявить операторы (не являющиеся членами) в том же пространстве имен, как класс, к которому они принадлежат.

За что-то вроде operator+, это довольно легко: он работает только на объектах FOO, поэтому он должен идти в той же пространстве имен, что и сам Foo. За operator<< и operator>>, вы все равно можете выбрать между пространствами имен std и bar. Отказ Прежде всего, вы не должны добавлять функцию / перегрузки оператора в пространство имен std. Отказ И во-вторых, важная часть этих перегрузок не в том, что они работают с потоком, но что они читают / пишут объект FOO. Так что это имеет больше смысла связываться с классом Foo.

Следует также отметить, что правила C ++ разработаны таким образом, чтобы перегруженные операторы, которые определяются в том же пространстве имен, что и класс, которым они работают, почти всегда будет правильно найден, в то время как это будет не так, если операторы будут объявлены в некоторых других, не связанных, пространство имен.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top