2017-02-21 45 views
1

Я получил кучу моих собственных объектов, которые я послушно положил в моем собственном пространстве имен:Утилиты, используемые в утилите, находятся в том же пространстве имен, что и используемые им типы?

namespace my { struct foo final {}; /* etc. */} 

Где я должен поставить не-другу функций, не являющихся членами (т.е., «глобальные» полезности подпрограмм) которые берут мои типы в качестве параметров? Должен ли я также поместить их в my имен

namespace my { extern void f(const foo&); } 

Или есть перевернутое (или падение), чтобы поместить их в глобальном пространстве имен

extern void f(const my::foo&); 

В любом случае, аргумент f является my::foo , так ли имеет значение, действительно ли сама функция называется ::f() или my::f()?

Edit: обратите внимание, что я специально не ищет «Мне нравится глобальный» или «Мне нравится в пространстве имен» (или аналогичный). Скорее, я ищу конкретные технические причины, чтобы предпочесть один подход по сравнению с другим (предполагая, что такие различия существуют на самом деле). Из комментария это звучит как один (??) Фактор для рассмотрения может быть ADL поведение; есть ли другие?

+0

Это вопрос, основанный на принципе мнения, и, таким образом, вне темы для этого сайта. Вы можете подумать о том, чтобы задать свой вопрос на [softwareengineering.stackexchange.com] (http://softwareengineering.stackexchange.com/) – Xirema

+3

Вводя их в том же пространстве имен, что и основной тип аргумента, позволяет ADL найти их, что удобно. Из-за аргументов нет/технических/проблем с их использованием в глобальном пространстве имен. Но когда разрешение имени не работает, эти нерелевантные функции могут эффективно создавать шум в диагностике. –

+1

@ Xirema Я не уверен, что это мнение основано; вполне могут быть технические причины предпочесть один подход по сравнению с другим. –

ответ

1

Ввод операций, тесно связанных с типом в том же пространстве имен, что и тип, разрешает поиск зависимых аргументов или поиск Koenig для работы.

Загрязнение глобального пространства имен часто является плохой идеей; вы получаете только одно глобальное пространство имен, и если ваш код не играет с тем, что кто-то еще там помещает, есть несколько решений.

Например, если у вас есть тип или функция с именем CString в глобальном пространстве имен, и вы пытаетесь включить и использовать код MFC, который вводит CString в глобальное пространство имен, вы ввернуты.

Код размещения в namespace означает, что вы загрязняете глобальное пространство имен одним токеном вместо одного имени функции и типа (и т. Д.).

Второй вещь, чтобы рассмотреть, если ваши тип template генерируются, помещая friend функции внутри template приводит к ADL-открываемому не- template функции, встречающиеся при взаимодействии с экземплярами класса экземпляров шаблона.

namespace my { 
    template<class T> 
    struct foo { 
    friend foo operator+(foo lhs, foo rhs) { return {lhs.x+rhs.x}; } 
    friend foo add(foo lhs, foo rhs) { return {lhs.x+rhs.x}; } 
    int x; 
    }; 
} 

Теперь my::foo f0{0},f1{1}; auto f2 = add(f0, f1); работает, как это делает auto f3=f0+f1;.

Оба add и operator+ здесь не являются шаблонами, что означает, если у нас есть тип, который преобразует-to foo, add(f0, converts_to_foo) также работает.

+0

Мне нравится ваш настоящий коротко-сладкий ответ: помещайте только один токен 'my' в глобальное пространство имен. И это звучит как с шаблонами (которые у меня есть в моем случае, но удалены в примере кода), вы «имеете», чтобы хранить вещи в пространстве имен. –

+0

@ Даан Нет, не «есть». Это метод, который я лично называю операторами Koenig, где вы используете свободные функции друзей, как указано выше, для расширения интерфейса типа в режиме ADL; такие имена находятся в охватывающем пространстве имен (sortof). – Yakk

1

Это зависит. Иногда у вас есть функция, которая принимает два аргумента из несвязанных пространств имен (например, в I/O или сериализации), он может находиться в одном из них (но не в глобальном пространстве имен!)

// foo can be either in N1 or N2, but not in global 
namespace N1 { auto foo(A&, N2::B const&) } 
namespace N2 { auto foo(N1::A&, B const&) } 

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

// algo should probably be in its own namespace N 
namespace N { 
    template<class R1, class R2> 
    auto algo(R1 const& in, R2& out) 
} 

Вызов такой алгоритм лучше всего сделать, не полагаясь на ADL, то есть, как N::algo(rin, rout).

+0

Вы говорите, что есть три варианта для 'foo()'? 1), как вы показали (глобальный), 2) 'namespace N1 {auto foo (A &, N2 :: B &)}' или 3) 'namespace N2 {auto foo (N1 :: A &, B&);}' –

+1

@Dan никогда не было в глобальном масштабе, я думал, что это самоочевидно. – TemplateRex

+0

ОК, получилось ... но «foo» можно было немного упростить, как я показал в зависимости от того, было ли это в «N1» или «N2», нет необходимо повторить закрывающее имя пространства имен. –