Пространство имен класса VERSUS, пространство имен OR и AND?

Оба класса и пространства имен?

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

Я полагаю, что вопрос верхнего уровня: это хорошая идея? Имея как класс, так и пространство имен для связанных понятий?

Вопрос нижнего уровня:

Каков наилучший способ сделать это?

Класс, вложенный в пространство имен?:

namespace Foo_Namespace {
   class Foo_Class {
       ...
   };
}

Или отдельный, одноранговый, класс и пространство имен?:

class Foo_Class {
    ...
};
namespace Foo_Namespace {
   // non-class free functions, etc.
}

Я должен признать, что я склоняюсь к вложению класса в пространство имен. Хотя это приводит к уродливым именам. Но даже если я это сделаю, какие соглашения об именах следует использовать:

Слишком длинное, что приводит к действительно уродливым именам Foo_Namespace:: Foo_Class

namespace Foo_Namespace {
   class Foo_Class {
       ...
   };
}

Нет необходимости использовать суффиксы или индикаторы в имени:

namespace Foo {
   class Foo {
       ...
   };
}

Но тогда я нахожу себя неуверенным, когда смотрю на Foo:: bar(), если это бесплатная функциональная панель в пространстве имен:: Foo, т.е.:: Foo:: bar() или функция-член в классе Foo в пространстве имен:: Foo:: Foo:: bar().

И имена вроде:: Foo:: Foo:: bar по-прежнему не являются, umm, nice.

В настоящее время я делаю

Нет необходимости использовать суффиксы или индикаторы в имени:

namespace Foo_ns {
   class Foo {
       ...
   };
}

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

Интересно, если я должен возродить соглашение об именах, которое я не использовал в годах: _c для класса, _ns для пространств имен:

namespace Foo_ns {
   class Foo_c {
       ...
   };
}

Детали:

Я не буду повторять то, что я сказал выше, но я добавлю немного больше.

Самая практическая причина, по которой я знаю, использовать пространство имен в дополнение к классу, заключается в том, что вам разрешено отправлять декларации свободных функций в пространстве имен, но вам не разрешено делать пересылку объявлений некоторых методов класса, То есть с классом вы должны объявить все или ничего. В то время как со свободными функциями вообще и свободными функциями в пространствах имен, в частности, вы можете объявить это по частям. Части могут быть объявлены в разных файлах заголовков. Вместо #, включая массивную библиотеку только для заголовка для массивного класса, вы можете использовать форвардные объявления только для одной или двух функций. Etc.

(см., например, http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Forward_Declarations, хотя Google беззаботно отказывается от форвардных деклараций, а не включает заголовок.)

Другая причина заключается в том, что пространство имен позволяет сохранить класс в целом.

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

Я предпочитаю, чтобы класс, вложенный в пространство имен, Foo_ns/Foo_ns:: Foo_c, вместо того, чтобы иметь их как peer Foo_ns/Foo_c, потому что часто классу нужны вспомогательные классы, например. Foo_ns/Foo_ns:: Foo_c/Foo_ns:: Foo_Helper_c. Если класс и пространство имен являются одноранговыми, кажется странным иметь Foo_ns/Foo_c, но Foo_ns:: Foo_Helper_c.

Мне нравятся пространства имен, потому что я согласен с Андреем Александресю: http://laser.inf.ethz.ch/2012/slides/Alexandrescu/1-C++%20course%20parts%201%20and%202.pdf

 Class methods vs. free functions

 • Conventional wisdom: methods are cool, free functions are so 1960s
 • Yet:
   ◦ Free functions improve encapsulation over methods
   ◦ Free functions may be more general
   ◦ Free functions decouple better
   ◦ Free functions support conversions on their left-hand argument

  Surprising fact #2

    Making a function a method should be
    your last, not first, choice

  (c) 2012– Andrei Alexandrescu. 32 / 59

Лучше создавать свободные функции, чем методы в классах.

Но иногда нужно просто использовать классы - например, для шаблонов.

Соглашение об именах мудрый

Я использовал Foo_ns/Foo_ns:: Foo_c в прошлом

Я использую Foo_ns/Foo_ns:: Foo now

(Кстати, я предпочитаю использовать Class_Names_With_Underscores_and_Initial_Caps, а не CamelCase.)

Если это имеет смысл, я могу исключить суффикс _ns в пространстве имен - например. где пространство имен и класс не должны иметь одинаковое имя.

Мне не нравится, что они имеют одно и то же имя. Рассмотрим конструктор для класса Foo внутри пространства имен foo:

:: Foo:: Foo:: Foo()  против  :: Foo_ns:: Foo:: Foo()

Последнее не намного лучше, но немного запутаннее.

Я думаю, что я обычно создаю класс сам по себе, не вложив его в пространство имен. Фактически, я, вероятно, добавляю несколько статических методов, прежде чем я пойму, что класс, вложенный в пространство имен, будет лучше. На этом этапе это может быть болью для рефакторинга, и иногда я создаю функции пересылки, которые переходят от статического метода класса к свободной функции в пространстве имен или наоборот. Это заставляет меня сожалеть о боте, чтобы перейти к классу с пространством имен прямо с шага 1.

Заключение

Мой текущий BKM - Foo_ns/Foo_ns:: Foo, т.е.

namespace Foo_ns {
   class Foo { 
   ...
   };
}

Буду признателен за любые предложения, любые улучшения.

Или я просто сломался для этого?

Ответ 1

Я рекомендую иметь ваш класс в пространстве имен со связанными функциями. Большая причина для этого - зависимый от аргумента поиск (ADL). Когда вы вызываете функцию, отличную от члена, которая имеет аргумент типа класса, имя функции просматривается в охватывающем пространстве имен этого класса. Поэтому, если у вас есть, скажите:

namespace foo {
  class bar { };
  void baz(bar);
}

Если вы когда-нибудь захотите позвонить baz, вам не нужно явно указывать пространство имен, в котором оно содержится, вы можете просто сделать:

foo::bar x;
baz(x);

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

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

Ответ 2

Отдельно Foo_Class и Foo_Namespace определенно неверно, это не позволит вам использовать зависящий от аргумента поиск, чтобы найти функции в пространстве имен, которые предназначены для использования с классом.

Таким образом, класс должен быть вложен в пространство имен, если в пространстве имен будут присутствовать функции, принимающие аргументы типа класса.

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

Также необычно назвать пространство имен с начальной буквой в верхнем регистре. Если имена классов начинаются с прописной буквы, вы можете перейти на пространство имен foo и класс foo, указав foo::Foo.

Не будет ли пространство имен содержать более одного класса? Это звучит необычно для меня. Я бы назвал пространство имен после всего его содержимого, просто не одного типа. Если это класс socket, я бы поставил его в пространство имен networking, например.

Я думаю, что суффиксы _c на классах совершенно смехотворны. Мне не нравится суффикс _ns на пространствах имен, его единственное сохранение - это то, что Foo_ns::Foo лучше, чем foo::Foo, но я все равно сделаю его foo::Foo.

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

Ответ 3

Мне не нравятся "фрактальные" имена, повторяющие вещи и пытающиеся избежать этого. Для вашего случая я бы попробовал что-то вроде пространства имен "Foo" и вложенного класса "Тип". Возможно, какое-то другое имя было бы более подходящим, но это зависит от вашего фактического использования. Вот пример использования, который я повторяю снова и снова:

namespace status {
    enum type {
        invalid,
        okay,
        on_fire
    };
    // debug output helper
    char const* to_string(type t);
}

Тип перечисления называется status::type, который может принимать значения, такие как status::okay. Довольно читаемый ИМХО.