Поради щодо просторів імен на C ++


75

Я просто навчаю себе просторам імен C ++ (що походять із фону C #) і справді починаю думати, що навіть незважаючи на те, що C ++ робить краще, ніж більшість інших мов, вкладені простори імен - не один з них!

Чи правильно я вважаю, що для того, щоб оголосити деякі вкладені простори імен, я повинен зробити наступне:

namespace tier1
{
    namespace tier2
    {
        namespace tier3
        {
            /* then start your normal code nesting */
        }
    }
}

На відміну від:

namespace tier1::tier2::tier3
{
}

а-ля C #?

Це стає ще більш незрозумілим, коли мені потрібно переслати заяву:

namespace tier1
{
    namespace tier2
    {
        namespace forward_declared_namespace
        {
            myType myVar; // forward declare
        }
        namespace tier3
        {
            /* then start your normal code nesting */
            class myClass
            {
                forward_declared_namespace::myType myMember;
            }
        }
    }
}

Маючи на увазі, що типова система, яку я розробляю, складається з:

MyCompany::MySolution::MyProject::System::[PossibleSections]::Type

Ось чому ви, як правило, не бачите великого використання просторів імен у прикладах на C ++? Або зазвичай лише поодинокі (не вкладені) простори імен?

ОНОВЛЕННЯ

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


8
Нарешті, C ++ 17 дозволить namespace tier1::tier2::tier3.
underscore_d

7
Посилання в оновленні порушено ...
AlwaysLearning

2
Мені було цікаво дізнатися, як ви вирішили проблему та натиснули ваше посилання. На жаль, як уже зазначав AlwaysLearning, посилання було порушено.
csj

Відповіді:


113

Простори імен С ++ не мали на меті бути механізмом проектування - вони існують просто для запобігання зіткненню імен. Ви дійсно не хочете і не повинні використовувати вкладені простори імен у 99,99% ситуацій.

Хорошим прикладом правильного використання просторів імен у C ++ є Стандартна бібліотека C ++. Все в цій досить великій бібліотеці розміщується в єдиному просторі імен, який називається std - немає спроб або необхідності розбити бібліотеку на (наприклад) простір підімен вводу-виводу, математичний простір імен, простір підімен контейнера тощо

Основним інструментом моделювання в C ++ є клас (і певною мірою шаблон), а не простір імен. Якщо ви відчуваєте потребу вкладання, вам слід розглянути використання вкладених класів, які мають наступні переваги перед просторами імен:

  • у них є методи
  • вони можуть контролювати доступ
  • їх неможливо відкрити повторно

Розглянувши їх, якщо ви все одно хочете використовувати вкладені простори імен неодмінно, зробіть це - технічно немає нічого поганого в тому, щоб використовувати їх таким чином.


18
шаблони не мали на меті бути механізмом mpl. Але хлопці використовують їх таким чином.
Микола Голуб’єв

13
Ех . . Я ненавиджу бути педантами (ми всі знаємо, що це брехня), але хіба <iostream> не реалізує простір імен ios всередині простору імен std? На даний момент у мене немає зручного компілятора C ++. . .
Binary Worrier

1
Не могли б ви надати аргументи (не приклади), чому не гарна ідея використовувати вкладені імена імен?
bayda

4
@binary ІОС є ЬурейеЕ для basic_ios <символ>

7
Простори імен у C ++ мають специфічні функції для того, щоб підтримувати інтерфейси більш модульним способом. Будь ласка, зверніться до таких правил у чудовій книзі "Стандарти кодування C ++" Герба Саттера та Андрея Александреску: 57: Зберігайте тип та його інтерфейс функції, що не є членом, в одному просторі імен. 58: Зберігайте типи та функції в окремих просторах імен, якщо вони спеціально не призначені для спільної роботи.
alexk7

23

Простори імен C ++ були значним покращенням порівняно з попередньою пропозицією (тобто просторів імен взагалі немає). Простори імен C # розширили концепцію та працювали з нею. Я б порадив вам зберігати свої простори імен у простій плоскій структурі.

РЕДАГУВАТИ Чи радите ви це через короткі побачення, які я зазначив тут?

Просто "Так". Простори імен C ++ не були розроблені, щоб допомогти вам розділити логіку та бібліотеки так, як це робиться в C #.

Призначення просторів імен С ++ - зупинити реальну проблему, з якою стикаються розробники С, коли вони стикаються із зіткненнями імен при використанні двох сторонніх бібліотек, які експортують однакові імена функцій. Розробники C мали різні способи вирішення цього питання, але це може бути серйозним болем.

Ідея полягала в тому, що STL і т.д. має std::простір імен, бібліотеки, надані "XYZ Corp", матимуть xyz::простір імен, а ви, що працюєте в "ABC corp", поміщали б усі ваші речі в єдиний abc::простір імен.


2
Ви радите це через короткі надходження, які я тут виклав?
Адам Нейлор,

Якщо багато людей працюють у "ABC corp" для багатьох тамтешніх проектів, і протягом багатьох років проекти зливаються або розпадаються, має сенс мати принаймні abc::PROJECTпростори підменів. І якщо великі проекти мають чіткі розділи, то це abc::PROJECT::SECTIONтакож має сенс. Навіть боги C ++ зрозуміли, що існують розділи, і створили простори підменів на зразок std::chronoor std::ios.
Kai Petzke

19

те, що я роблю, коли оголошення вперед виглядає так:

 namespace abc { namespace sub { namespace subsub { class MyClass; }}}

Мої попередні декларації зведено в один рядок. Читаність прямого оголошення декларується в обмін на читабельність решти коду. І для визначень я також не використовую відступи:

 namespace abc {
 namespace sub {
 namespace subsub {
 
 class MyClass 
 {
    public:
       MyClass();

       void normalIntendationsHere() const;
 };

 }
 }
 }

Спочатку використання цього стилю вимагає трохи дисципліни, але для мене це найкращий компроміс.


З C ++ 17 ви можете оголосити простір імен із синтаксисом, запропонованим автором питання.

namespace A::B::C { ... }

визначення вкладеного простору імен: namespace A::B::C { ... }еквівалентно namespace A { namespace B { namespace C { ... } } }.

https://en.cppreference.com/w/cpp/language/namespace


7
Це не відповідь на поставлене запитання.
Paul Merrill

2
@Jammer: Тоді я був новачком у stackoverflow і не мав ідеї :) Sry.
doc

8

По крайней мере, як невелика допомога, в деяких випадках ви можете зробити це:

namespace foo = A::B::C::D;

А потім посилання A :: B :: C :: D як foo. Але лише в деяких випадках.


2
У яких випадках ви цього не можете зробити?
Michael Dorst

Справа у питанні одна. Я думаю?
Гонки легкості на орбіті

6

Ви можете пропустити відступ. Я часто пишу

namespace myLib { namespace details {

/* source code */

} } /* myLib::details */

Вихідний код C ++ згодом компілюється у двійковий файл, на відміну від C # / Java, який залишається в двійковому файлі. Отже, простор імен просто пропонує чудове рішення для конфлікту імен змінних. Він не призначений для ієрархії класів.

Я часто зберігаю в коді один-два рівні простору імен.


4

Перш за все, ви можете уникнути відступу простору імен, оскільки для цього немає причин.

Використання просторів імен у прикладах не покаже потужності просторів імен. І їхня сила, як на мене, полягає в розділенні областей домену одна від іншої. Розділіть корисні класи від ділових.

Просто не змішуйте різні ієрархії простору імен в одному файлі .h. Простори імен - це свого роду додатковий коментар для вашого інтерфейсу оголошення функцій. Перегляд просторів імен та назв класів повинен пояснити багато речей.

namespace product
{
namespace DAO
{

class Entity
{
};

3
"Перш за все, ви можете уникнути відступу простору імен, оскільки для цього немає причин." це саме те, що я насправді роблю;)
Адам Нейлор

Чудово, і я думаю, що потужність простору імен ще більше посилюється при використанні інструментів CASE для візуалізації взаємозв’язків базової коду та шарів. Такі інструменти, як CppDepend або Графік залежностей Visual Studio, беруть за основу такі «захоплення зрозумілості коду», які структуровані за допомогою всіх вкладених просторів імен, щоб реально висвітлити шари та залежності в добре структурованій послідовній манері.
jxramos

0

Ви надмірно їх використовуєте (і взамін не отримаєте нічого).


15
Ієрархічна, впорядкована структура. (повторно "ви нічого не отримаєте натомість"). Я думаю, що це величезне і значно недооцінене в C ++. Подумайте лише, як це впливає на документацію.
Конрад Рудольф

1
@Konrad, я вважаю, що це суть мого запитання.
Адам Нейлор,

Питання дуже давнє, але воно все ще актуальне для мене сьогодні. У мене була така ж думка, як у @KonradRudolph, але потім я виявив, що вкладені простори імен нічого не варті . Найкращий спосіб - це просто плоский широкий простір імен бібліотеки ... Це сумно, але найкраще, що ми маємо зараз. І це слідує за способом структури std. І це структуровано таким чином з причини - кращого способу в даний час немає.
Sahsahae

@Sahsahae Вони, безумовно, нічого не варті, навіть на думку (!) Одного старшого члена комітету зі стандартів С ++. Але навіть тоді це лише така: думка (яку я не поділяю). Я точно не рекомендую вкладати легковажно - але розумне використання вкладених просторів імен широко і успішно використовується іншими мовами, і не демонструє дефектів, які створює Тит.
Конрад Рудольф

@KonradRudolph Ось ваше питання: інші мови. Це питання стосувалося просторів просторів імен C ++, і я не розширював сферу його застосування. Як не дивно, так само, як і простори імен С ++, мені випадково вдалося зараз поширити його на "простір імен на всіх мовах", тому що я langs::cppвсе ще можу бачити все langs, без реальної ізоляції просто cpp... Це саме проблема з простором імен С ++ і чому пропонується плоска структура. Наприклад, у модулях Rust ви не могли отримати доступ langs::*з cpp, і я ніколи не говорив, що це погано, це здорово, але в C ++ це не те.
Sahsahae

0

Я виявив, що ви можете сортувати простір імен c # таким чином;

namespace ABC_Maths{
    class POINT2{};
    class Complex{};
}

namespace ABC_Maths_Conversion{
    ABC_MATHS::Complex ComplexFromPOINT2(ABC_MATHS::POINT2)
    {return new ABC_MATHS::Complex();}

    ABC_MATHS::POINT4 POINT2FromComplex(ABC_MATHS::COMPLEX)
    {return new ABC_MATHS::POINT2();}
}

namespace ABC
{
}

Але код, здається, не дуже охайний. і я очікував би тривалого використання

Краще вкласти якомога більше функціональних можливостей у класи щось на зразок

namespace ABC{
    class Maths{
        public:
        class POINT2{};
        class Complex:POINT2{};
        class Conversion{
            public:
            static Maths.Complex ComplexFromPOINT2(MATHS.POINT2 p)
            {return new MATHS.Complex();}

            static MATHS.POINT2 POINT2FromComplex(MATHS.COMPLEX p)
            {return new ABC::MATHS.POINT2();}// Can reference via the namespace if needed
} /*end ABC namespace*/

І це все ще трохи довго. але відчуває себе трохи ОО.

І чує, як це здається найкращим чином зробити

namespace ABC
{
    class POINT2{};
    class Complex:POINT2{};
    Complex ComplexFromPOINT2(POINT2 p){return new Complex();}
    POINT2 POINT2FromComplex(Complex){return new POINT2();}
}

чує, як би виглядали звички

int main()
{
    ABC_Maths::Complex p = ABC_Maths_Conversion::ComplexFromPOINT2(new ABC_MATHS::POINT2());

    // or THE CLASS WAY

    ABC.Maths.Complex p = ABC.Maths.Conversion.ComplexFromPOINT2(new ABC.Maths.POINT2());

    // or if in/using the ABC namespace

    Maths.Complex p = Maths.Conversion.ComplexFromPOINT2(new Maths.POINT2());

    // and in the final case

    ABC::Complex p = ABC::ComplexFromPOINT2(new ABC::POINT2());
}

було цікаво з’ясувати, чому я ніколи не використовував простори імен c ++, як це робив із c #. Це було б занадто довго, і ніколи не працювало б так само, як простори імен c #.

найкраще використання для просторів імен в c ++ - це зупинити, скажімо, мою функцію super cout (яка відключається кожного разу, коли її викликають), щоб вона не змішувалася з функцією std :: cout (що набагато менше вражає).

Те, що c # та c ++ мають простори імен, це не означає, що простір імен означає одне і те ж. вони різні, але схожі. Ідея просторів імен c #, мабуть, виникла з просторів імен c ++. хтось, мабуть, бачив, що може зробити подібне, але інше, і йому не вистачало сили фантазії, щоб дати йому свою власну оригінальну назву, як "ClassPath", що набагато більше має сенсу, оскільки це шлях до занять, а не для забезпечення іменування просторів, де кожен пробіл може мати однакові імена

Сподіваюся, це комусь допомагає

Я забув сказати, що всі ці способи є дійсними, і помірне використання перших двох можна включити до третього, щоб створити бібліотеку, яка має сенс, тобто (не чудовий приклад)

_INT::POINT2{}

і

_DOUBLE::POINT2{}

так що ви можете змінити рівень точності за допомогою

#define PREC _DOUBLE 
// or #define PREC _INT 

а потім створити екземпляр PREC :: POINT2 для подвійної точності POINT2

З просторами імен c # або java це зробити непросто

очевидно, це лише один приклад. Подумайте, як читалося використання.


-1

Іноді я оголошую глибокі простори імен як пару макросів в окремому заголовку

простір імен. h

#define NAMESPACE_TIER1_TIER2_TIER3 \
    namespace tier1 { \
    namespace tier2 { \
    namespace tier3 {

#define END_NAMESPACE_TIER1_TIER2_TIER3 }}}

Щоб використовувати його десь ще:

іншийфайл.h

#include "./namespace.h"

NAMESPACE_TIER1_TIER2_TIER3;

/* Code here */

END_NAMESPACE_TIER1_TIER2_TIER3;

Надлишкові крапки з комою після макросу мають уникнути зайвого відступу.


2
це дуже брудно, і вам доведеться визначати його для кожного вкладеного простору імен. плюс, якщо ви збираєтеся виписати NAMESPACE_TIER1_TIER2_TIER3і END_NAMESPACE_TEIR1_TIER2_TIER3, чому б просто не написати namespace tier1{namespace tier2{namespace tier3{і }}}? Разом це насправді коротше.
Michael Dorst

Я не згоден, це не коротше, і це створює дуже глибокі проблеми з відступами. Я спробував і те, і інше, і я віддаю перевагу методу #define. Змінити: добре, мінус проблема з відступами ...
морозиво

1
Віддавайте перевагу тому, що вам подобається, але я не рекомендував би це іншим користувачам. Це не коротше (як видно з мого коментаря, де вони вишикувалися), і це не враховуючи визначення, і, звичайно, складніше відстежувати помилки таким чином. Що робити, якщо ви робите друкарську помилку? Компілятор не знатиме, що не так, і ваша помилка з’явиться зовсім не в тому місці.
Michael Dorst

3
Аргумент "коротший" насправді не діє, оскільки він залежить від способу визначення макросів. І про аргумент "друкарська помилка". Напишіть неправильне ім'я простору імен, і ви отримаєте такі ж загадкові повідомлення, як і при вирішенні макросів, але за допомогою рішення макросів багато сучасних середовищ IDE можуть вирішити, що ім’я не було визначено, і допомогти вам знайти помилку. Назва простору імен - це просто вільний текст, і ви можете писати туди все, що завгодно, тому насправді важче уникнути проблем із друкарською помилкою, коли не використовуєте макрос. І немає шкоди в тому, щоб "рекомендувати" макророзв'язок, люди можуть вирішити самі.
морозиво
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.