Чому "використовується простір імен std;" вважається поганою практикою?


2639

Інші мені сказали, що писати using namespace std;в коді неправильно, і що я повинен використовувати std::coutі std::cinбезпосередньо замість цього.

Чому це using namespace std;вважається поганою практикою? Це неефективно чи він ризикує оголосити неоднозначні змінні (змінні, які мають те саме ім’я, що і функція в stdпросторі імен)? Чи впливає це на ефективність роботи?


512
Не забувайте, що ви можете зробити: "using std :: cout;" що означає, що вам не потрібно вводити std :: cout, але не вносити водночас весь простір імен std.
Білл

2
@a платний ботанік google-styleguide.googlecode.com/svn/trunk/… посилання більше не працює. Здається, що нове посилання - google.github.io/styleguide/cppguide.html#Other_C++_Features
MCG

64
Особливо погано використовувати "використання простору імен std" в області застосування файлів у заголовках. Використання його у вихідних файлах (* .cpp) в масштабі файлу все-таки включає не так вже й погано, оскільки його ефект обмежений однією одиницею перекладу. Ще менш проблематичним є використання його всередині функцій або класів, оскільки його дія обмежена сферою функції або класу.
ш-

5
Я б збентежити використовувати директиву , але для певних просторів імен подобаються std::literals::chrono_literals, Poco::Data:Keywords, Poco::Unitsі речі , які будуть мати справу з літералами або читаність трюками. Кожен раз, коли він знаходиться у заголовках чи файлах реалізації. Можливо, в області функцій, можливо, гадаю, але, крім літералів та інших матеріалів, це не корисно.
Ludovic Zenohate Lagouardette

7
@Jon: Це не має нічого спільного з простором імен std, зокрема. Мій акцент мався на увазі "на області дії файлів у заголовках". Поставляючи це як пораду: не використовуйте "використання простору імен" (std чи інше) в області застосування файлів у заголовкових файлах. Добре використовувати його у файлах реалізації. Вибачте за неоднозначність.
ш-

Відповіді:


2229

Це взагалі не пов’язано з продуктивністю. Але врахуйте це: ви використовуєте дві бібліотеки під назвою Foo і Bar:

using namespace foo;
using namespace bar;

Все працює добре, і ви можете без проблем зателефонувати Blah()з Foo та Quux()з Бару. Але одного разу ви оновите до нової версії Foo 2.0, яка тепер пропонує функцію під назвою Quux(). Тепер у вас конфлікт: і Foo 2.0, і Bar імпортуються Quux()у ваше глобальне простір імен. Це потребує певних зусиль для виправлення, особливо якщо параметри функції збігаються.

Якби ви використовували foo::Blah()та bar::Quux(), то введення foo::Quux()було б не події.


435
Мені завжди подобалося Python "імпортувати big_honkin_name як bhn", тож ви можете просто використовувати "bhn.something", а не "big_honkin_name.something" - справді скорочується при наборі тексту. Чи є у C ++ щось подібне?
paxdiablo

764
@Pax простір імен io = boost :: файлова система;
AraK

152
Я думаю, що це завищення речей, щоб сказати, що це "певні зусилля, щоб виправити". У вас не буде жодних примірників нового foo :: Quux, тому просто розберіть всі ваші поточні користування за допомогою bar :: Quux.
MattyT

289
Чи створить будь-яку розсудливу людину бібліотеку з типами, у яких некваліфіковане ім’я стикається з типами std?
erikkallen

94
@TomA: Проблема #defineполягає в тому, що він не обмежується просторами імен, а топче всю базу коду. Псевдонім простору імен - це те, що ви хочете.
sbi

1390

Я погоджуюся з усім, що написав Грег , але хотів би додати: Це може навіть стати гіршим, ніж сказав Грег!

Бібліотека Foo 2.0 може запровадити функцію, Quux()яка є однозначно кращою відповідністю для деяких ваших дзвінків, Quux()ніж bar::Quux()ваш код, який викликається роками. Тоді ваш код все ще компілюється , але він мовчки викликає неправильну функцію і робить бог-знає-що. Це приблизно так само погано, як все може отримати.

Майте на увазі , що stdпростір імен має тонн ідентифікаторів, багато з яких є дуже поширеними з них (думаю list, sort, string,iterator і т.д.) , які дуже ймовірно, з'явиться в іншому коді теж.

Якщо ви вважаєте це малоймовірним: тут було задано запитання на Stack Overflow, де майже це сталося (неправильна функція, викликана через опущений std::префікс), приблизно через півроку після того, як я дав цю відповідь. Ось ще один, останній приклад такого питання. Тож це справжня проблема.


Ось ще один інформаційний момент: Багато, багато років тому мені також було здається, що це дратує, що потрібно перефіксувати все із стандартної бібліотеки std::. Тоді я працював над проектом, де на початку було вирішено, що і usingдирективи, і декларації заборонені, за винятком областей функціонування. Вгадай що? Більшості з нас знадобилося дуже мало тижнів, щоб звикнути писати префікс, а через кілька тижнів більшість з нас навіть погодилися, що він фактично зробив код більш читабельним . Для цього є причина: чи подобається вам коротша чи довша проза, це суб'єктивно, але префікси об'єктивно додають ясності коду. Не тільки компілятор, але і вам легше зрозуміти, до якого ідентифікатора йдеться.

За десятиліття цей проект виріс до кількох мільйонів рядків коду. Оскільки ці дискусії виникають знову і знову, мені одного разу було цікаво, як часто (дозволений) функціональний діапазон usingнасправді використовується в проекті. Я проглянув джерела для цього і знайшов лише один-два десятки місць, де він використовувався. Для мене це вказує на те, що колись спробували, розробники не вважають std::достатньо болючим використовувати директиви навіть раз на 100 kLoC, навіть там, де це було дозволено використовувати.


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


140
Це суттєво шкодить щільності коду, який можна упакувати в один рядок. Ви в кінцевому підсумку пишете свій код дуже довго. що знижує читабельність. Особисто я вважаю, що коротший (але не надто короткий) код має тенденцію до більш читабельності (оскільки читати менше матеріалів і менше відволікатися).
Лежати Райан

91
Здогадайтесь, ви пропустили старі часи, перш ніж C ++ мав стандартний stringклас, і, здається, кожна бібліотека мала свій власний. Скажіть, що: ми продовжуємо писати наш код std::, і ви можете запускати наш код, grep -v std:: | vimколи ви переглядаєте його. Або ви можете навчити свого редактора std::ключовому слову, яке має бути кольоровим так само, як колір фону. Що б не працювало.
Майк Десімоне,

79
Я не думаю, що std::це взагалі шкідливо. Він несе в собі дуже важливу інформацію (а саме "все, що відбувається, є частиною стандартної бібліотеки", і це все ще досить короткий і компактний префікс. Більшість часу це зовсім не проблема. Іноді у вас є кілька рядків коду де ви повинні посилатися на конкретні символи в stdпросторі імен багато, а потім using. заяву , в цій конкретній галузі видимості вирішує проблему красиво Але в загальному випадку, це не шум, він передає цінну інформацію в доповненні до видалення двозначностей.
jalf

146
Щоразу, коли я бачу std::, я знаю, що це буде з того, що std::не потрібно думати про це. Якщо я бачу stringчи listабо mapсамі по собі, трохи дивуюсь.
Mateen Ulhaq

67
@LieRyan Тоді удачі написати бібліотеку геометрії, ніколи нічого не називаючи vector, transformабо distance. І це лише приклади багатьох багатьох дуже поширених імен, використовуваних у стандартній бібліотеці. Запропонувати не використовувати їх із-за страху чи упередженої думки щодо функції простору імен, яка є невід’ємною частиною C ++, є досить контрпродуктивною.
Крістіан Рау

419

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

Однак, можливо, ви можете розмістити оператор у своїх (приватних) * .cpp-файлах.


Будьте уважні, що деякі люди не згодні з моєю приказкою "почувайтеся вільно", як це - тому що, хоча usingвисловлення у файлі cpp краще, ніж у заголовку (тому що це не впливає на людей, які включають файл заголовка), вони думають, що це все ще не так добре (адже залежно від коду це може ускладнити реалізацію класу у підтримці). Цей запис C ++ Super-FAQ говорить:

Використовувана директива існує для застарілого коду C ++ і для полегшення переходу до просторів імен, але ви, ймовірно, не повинні користуватися нею регулярно, принаймні, не у новому коді C ++.

FAQ пропонує дві альтернативи:

  • Декларація використання:

    using std::cout; // a using-declaration lets you use cout without qualification
    cout << "Values:";
  • Просто наберіть std ::

    std::cout << "Values:";

1
Звичайно, ви ніколи не повинні припускати стан глобального кута, щоб у когось немає std: cout << std :: hex і не вдалося std :: Resto_cout_state згодом. Але це зовсім інший фітберг.
Móż

233

Нещодавно я зіткнувся зі скаргою на Visual Studio 2010 . Виявилося, що майже всі вихідні файли мали ці два рядки:

using namespace std;
using namespace boost;

Багато функцій Boost переходять у стандарт C ++ 0x, а Visual Studio 2010 має багато C ++ 0x функцій, тому раптом ці програми не збиралися.

Таким чином, уникнення using namespace X;- це форма захищеності від майбутнього, спосіб переконатися, що зміна використаних бібліотек та / або файлів заголовків не порушує програму.


14
Це. Boost і std мають багато перекриттів - тим більше, що C ++ 11.
einpoklum

1
Я зробив це один раз і засвоїв урок важким шляхом. Зараз я ніколи не використовую usingпоза визначенням функції і рідко використовую using namespaceвзагалі.
Ферруччо

210

Коротка версія: не використовуйте глобальних usingдекларацій чи директив у файлах заголовків. Сміливо використовуйте їх у файлах реалізації. Ось що Герб Саттер та Андрій Олександреску мають сказати з цього приводу у стандартах кодування C ++ (підкреслюючи акценти - це моє):

Підсумок

Використання простору імен - це для вашої зручності, а не для того, щоб наносити іншим: Ніколи не пишіть декларацію, що використовує, чи директиву використання перед директивою #include.

Висновок: у файлах заголовків не пишіть на рівні простору імен, використовуючи директиви або використовуючи декларації; натомість, чітко простір імен - кваліфікуйте всі імена. (Друге правило випливає з першого, оскільки заголовки ніколи не можуть знати, які інші заголовки #includes можуть з'являтися після них.)

Обговорення

Коротше кажучи: Ви можете і повинні використовувати простір імен, використовуючи декларації та директиви вільно у файлах вашої реалізації після директив #include, і відчувати себе добре. Незважаючи на неодноразові твердження про протилежне, простір імен з використанням декларацій та директив не є злим, і вони не перешкоджають призначенню просторів імен. Швидше, саме вони роблять простори імен корисними .


4
Тут ще одна думка програміста, але, хоча я на 100% погоджуюся з твердженням, що слово usingніколи не повинно з’являтися в заголовку, я не настільки переконаний у вільній ліцензії на розміщення в using namespace xyz;будь-якому місці вашого коду, особливо якщо xyzє std. Я використовую using std::vector;форму, оскільки це витягує лише один елемент із простору імен у псевдо глобальний обсяг, тому призводить до набагато меншого ризику зіткнення.
dgnuff

2
@Lightness Races Orbit ви, звичайно, маєте право на вашу думку. Було б корисніше, якби була спроба пояснення, чому ви не згодні з порадами, наданими у цій відповіді. Особливо було б цікаво зрозуміти, у чому сенс просторів імен, якщо "використовувати" їх погано? Чому б не просто назвати речі std_cout замість std :: cout ... творці C ++ / простору імен, мабуть, мали уявлення, коли вони намагалися їх створити.
nyholku

1
@nyholku: Не потрібно - більшість інших відповідей дають ті самі причини, що і я. Також, будь ласка, не соромтеся зазначити ":)" Я додав до свого коментаря! І що я не сказав, що простори імен погані.
Гонки легкості по орбіті

Так, я помітив це :), але ІМО, більшість відповідей (що суперечать цій мудреці поради) помилково (не те, що я робив статистику, яка є більшістю зараз). Якщо ви згодні з тим, що простір імен "непоганий", ви можете сказати, де ви вважаєте, що вони підходять, якщо ви не згодні з цією відповіддю?
nyholku

Я не можу не відчути, що using namespaceтаке зло, як gotoзло. Обидва мають дійсне використання, але 999 разів з 1000 вони будуть використані неправильно. Так, так, з using namespaceджерелом ви не забруднити простір імен інших включених, акуратних. Але це все одно не захистить вас від "веселощів", які виникають із using namespace Foo+ using namespace Barз вами дзвінками (неявне Foo: :) baz(xyz)і раптом порушення коду (без пов'язаних змін), просто тому, що Bar::baz()десь додано, що просто стане кращим матч (і таким чином тепер називається замість нього)
CharonX

122

Не слід використовувати usingдирективу в глобальному масштабі, особливо в заголовках. Однак є ситуації, коли це доречно навіть у файлі заголовка:

template <typename FloatType> inline
FloatType compute_something(FloatType x)
{
    using namespace std; // No problem since scope is limited
    return exp(x) * (sin(x) - cos(x * 2) + sin(x * 3) - cos(x * 4));
}

Це краще, ніж явна кваліфікація ( std::sin, std::cos...), оскільки вона коротша і має можливість працювати з визначеними користувачем типами з плаваючою точкою (через пошук аргументів (ADL)).


9
Перепрошую, але я категорично не згоден з цим.
Біллі ONeal

4
@Billy: Немає іншого способу підтримати виклик userlib :: cos (userlib :: superint). Кожна функція має користь.
Zan Lynx

17
@Zan: Звичайно, є. using std::cos;, using std::sinі т. д. Проблема в тому, що будь-який добре розроблений userlibтакож матиме своє sinі cosвсередині власного простору імен, тому це вам справді не допоможе. (Якщо немає using namespace userlibцього шаблону, і це так само погано, як using namespace std- і сфера його дії не обмежена.) Крім того, єдина така функція, яку я коли-небудь бачу, це відбувається swap, і в таких випадках я б рекомендував просто створити шаблон. спеціалізація std::swapта уникнення всієї проблеми.
Біллі ONeal

11
@BillyONeal: template<typename T> void swap(MyContainer<T>&, MyContainer<T>&)(Немає жодної часткової спеціалізації шаблону функцій (FTPS), тому іноді потрібно вдатися до перевантаження, замість цього
sbi

38
@BillyONeal: Ваш (7-кратний відгук!) Коментар невірний - ситуація, яку ви описуєте, саме те , що ADL було розраховано. Коротше кажучи, якщо xє один або більше «пов'язані простору імен» (наприклад , якщо він був визначений в namespace userlib) то будь-який виклик функції , яка виглядає як cos(x)буде додатково шукати в цих просторах імен - без будь - яких using namespace userlib;заздалегідь бути необхідним. Zan Lynx має рацію (а пошук імені C ++ - візантійський ...)
j_random_hacker

97

Не використовуйте його у всьому світі

Він вважається "поганим" лише тоді, коли використовується у всьому світі . Тому що:

  • Ви захаращуєте простір імен, в якому програмуєте.
  • Читачі матимуть труднощі побачити, звідки береться той чи інший ідентифікатор, коли ви користуєтесь багатьма using namespace xyz.
  • Що б не було правдою для інших читачів вашого вихідного коду, це ще більше справедливо для самого його читача: вас самих. Поверніться через рік-два і подивіться ...
  • Якщо ви говорите лише про себе, using namespace stdви можете не знати про всі речі, які ви захоплюєте - а коли ви додасте інше #includeабо переходите до нової версії C ++, у вас можуть виникнути конфлікти імен, про які ви не знали.

Ви можете використовувати його локально

Ідіть вперед і використовуйте його локально (майже) вільно. Це, звичайно, заважає повторитиstd:: - і повторення також погано.

Ідіома для використання її локально

У C ++ 03 була ідіома - код коробки, - для реалізації swapфункції для ваших класів. Було запропоновано фактично використовувати локальний using namespace std- або принаймні using std::swap:

class Thing {
    int    value_;
    Child  child_;
public:
    // ...
    friend void swap(Thing &a, Thing &b);
};
void swap(Thing &a, Thing &b) {
    using namespace std;      // make `std::swap` available
    // swap all members
    swap(a.value_, b.value_); // `std::stwap(int, int)`
    swap(a.child_, b.child_); // `swap(Child&,Child&)` or `std::swap(...)`
}

Це робить наступну магію:

  • Компілятор вибере std::swapдля value_, тобто void std::swap(int, int).
  • Якщо у вас void swap(Child&, Child&)реалізовано перевантаження, компілятор вибере його.
  • Якщо ви НЕ маєте , що перевантаження компілятор буде використовувати void std::swap(Child&,Child&)і спробувати всі ці заміни.

З C ++ 11 більше немає причин використовувати цю схему. Реалізацію std::swapбуло змінено, щоб знайти потенційну перевантаження та вибрати її.


5
"Реалізацію std :: swap було змінено, щоб знайти потенційну перевантаження та вибрати її." - Що? Ви впевнені в цьому? Хоча це правда, що надання послуг swapв першу чергу вже не так важливо в C ++ 11, оскільки std::swapсаме воно є більш гнучким (використовує семантику переміщення). Але std::swapавтоматично обирати власний власний своп, це абсолютно нове для мене (і я не дуже вірю в це).
Крістіан Рау

@ChristianRau Я так думаю, так. Я десь читав це на SO. Ми завжди можемо запитати Говарда , він повинен знати. Я копати і копати зараз ...
towi

14
Навіть у випадку свопу, більш чіткий (і на щастя більш поширений) ідіома - це писати, using std::swap;а не using namespace std;. Більш конкретна ідіома має менше побічних ефектів, а тому робить код більш доцільним.
Адріан Маккарті

11
Заключне речення неправильне. У C ++ 11 Std Swap Two Step був офіційно освячений як правильний спосіб виклику swap, і різні інші місця в стандарті були змінені, щоб сказати, що вони дзвонять swapтак (NB, як зазначено вище, using std::swapце правильний шлях, ні using namespace std). Але std::swapсама по собі рішуче не змінилася, щоб знайти якусь іншу swapі використати її. Якщо std::swapдзвонить, то std::swapзвикає.
Джонатан Уейклі

3
using std::swapОднак, було б розумніше просто ввести локально, щоб зменшити локальний простір імен, одночасно створивши код самодокументування. Вас рідко цікавить весь простір імен std, тому просто виберіть ті частини, які вас цікавлять.
Lundin

79

Якщо імпортувати потрібні файли заголовків ви раптом імена , як hex, left, plusабо countв вашому глобальному масштабі. Це може бути дивно, якщо ви не знаєте, що std::містять ці імена. Якщо ви також спробуєте використовувати ці назви локально, це може призвести до певної плутанини.

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


12
+1 не кажучи вже про distance. все ще я віддаю перевагу некваліфікованим іменам, де це практично можливо, оскільки це збільшує читабельність для мене. плюс, я думаю, той факт, що ми зазвичай не кваліфікуємо речі в усному мовленні і готові витрачати час на вирішення можливих неясностей, означає, що це цінність, щоб зрозуміти, про що йдеться, без кваліфікації, і застосувати до джерела код, що означає, що він структурований таким чином, що зрозуміло, про що йдеться навіть без кваліфікації.
ура та хт. - Альф

Якщо бути справедливим, у вас немає більшості з них, якщо ви не включите їх <iomanip>. Все-таки хороший момент.
einpoklum

48

Ще одна причина - сюрприз.

Якщо я бачу cout << blah, замість цього std::cout << blahдумаю: Що це cout? Це нормально cout? Це щось особливе?


25
Це жарт? Я справді не можу сказати. Якщо ні, то я особисто припускаю, що це нормальна 'cout', якщо ви не довіряєте коду, оскільки в іншому випадку це буде запахом коду «МІЖ», IMO. ... І якщо ви не довіряєте коду, то для чого ви його використовуєте в першу чергу? Зауважте, що я не кажу "ДОБРІТЬ ВСЕ! !!" але це також здається трохи надуманим, якщо ви, скажімо, маєте справу з якоюсь відомою бібліотекою від GitHub чи іншим.
Brent Rittenhouse

28
@BrentRittenhouse cout- поганий приклад, тому що всі це визнають. Але уявіть futureу фінансовому додатку. Це договір на купівлю чи продаж чогось у визначений день? Ні, це не так. Якби код сказав, std::futureвас не так легко переплутати.
Джеймс Холліс

2
@BrentRittenhouse, можливо, трохи поганий приклад, є щонайменше чотири різні бібліотеки, які мають cout. Може бути "це стандартна бібліотека? Libstdc ++? Stl? Щось інше?" І ні, не всі знають std :: cout, принаймні по суті, 6 із 7 нових працівників, яких ми отримуємо, не роблять. Тому що навчальні програми не використовують цих в освіті. Я маю виганяти друковані файли. Або налагодження () - від Qt.
Свіфт - П’ятничний пиріг

1
Дійсно? Це майже в першому прикладі першої глави Sooo багато книг на C ++, якщо що-небудь це (з використанням оператора вставки) - єдиний C ++, який деякі нові боди знають.
mckenzm

@mckenzm Я міг би помістити це в книзі чи лекційних конспектах, щоб зменшити безлад, але не в коді
Мартін Бекетт

45

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

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

Які ці вагомі причини ? Іноді програмісти явно хочуть вимкнути ADL, в інший раз вони хочуть роз'єднати.

Отже, все в порядку:

  1. Використання директив на рівні функцій та використання декларацій всередині реалізації функцій
  2. Рівень вихідного файлу, використовуючи декларації всередині вихідних файлів
  3. (Іноді) використовуючи директиви на рівні вихідного файлу

43

Я погоджуюся, що його не слід використовувати в усьому світі, але це не так зло, щоб використовувати локально, як в а namespace. Ось приклад з "Мова програмування на C ++" :

namespace My_lib {

    using namespace His_lib; // Everything from His_lib
    using namespace Her_lib; // Everything from Her_lib

    using His_lib::String; // Resolve potential clash in favor of His_lib
    using Her_lib::Vector; // Resolve potential clash in favor of Her_lib

}

У цьому прикладі ми вирішили можливі зіткнення імен та неоднозначності, що виникають із їх складу.

Імена, явно оголошені там (включаючи імена, оголошені за допомогою декларацій типу His_lib::String), мають пріоритет над іменами, доступними в іншому обсязі за допомогою директиви використання ( using namespace Her_lib).


29

Я також вважаю це поганою практикою. Чому? Лише одного дня я подумав, що функція простору імен - це поділ речей, тому я не повинен її псувати, кидаючи все в один глобальний мішок.

Однак, якщо я часто використовую 'cout' і 'cin', я пишу: using std::cout; using std::cin;у .cpp-файл (ніколи у файлі заголовка, оскільки він поширюється разом із #include). Я думаю , що жодна розсудлива ніколи не буде називати потік coutабо cin. ;)


7
Це місцеве використання декларації , зовсім інше, ніж використання директиви .
sbi

25

Приємно бачити код і знати, що він робить. Якщо я бачу, std::coutя знаю, що це coutпотік stdбібліотеки. Якщо я бачу, coutто не знаю. Це може бути coutпотік stdбібліотеки. Або може бути на int cout = 0;десять рядків вище в тій же функції. Або staticзмінна, названа coutу цьому файлі. Це може бути що завгодно.

Тепер візьміть мільйонну базу кодових рядків, яка не особливо велика, і ви шукаєте помилку, а значить, ви знаєте, що в цьому мільйоні рядків є одна лінія, яка не робить те, що потрібно робити. cout << 1;міг прочитати static intназваний cout, перенести його наліво на один біт і викинути результат. Шукаю помилку, я повинен був би це перевірити. Ви можете бачити, як я насправді волію бачити std::cout?

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


4
Звідки ви знаєте, що "std :: cout << 1" не читає статичний int з ім'ям cout у просторі імен std, переміщуючи його на один і викидаючи результат? Також як ти знаєш, що робить "<<";) ??? ... здається, що ця відповідь не є хорошою точкою даних, щоб уникнути "використання".
nyholku

4
Якщо хтось переосмислив std :: cout як ціле число, то ваша проблема не технічна, а соціальна - хтось має це для вас. (і ви, ймовірно, також повинні перевірити всі заголовки на предмет таких предметів, як #define true false тощо)
Джеремі Фріснер

2
Коли я бачу cout, я знаю, що це std :: cout, завжди. Якщо я помиляюся, це проблема людини, яка написала цей код, а не я :)
Tien Do

22

Вся справа в управлінні складністю. Використання простору імен призведе до того, що ви не хочете, і, таким чином, ускладнюватимуть налагодження (я кажу, можливо). Використання std :: у всьому світі важче читати (більше тексту та все таке).

Коні на курси - керуйте своєю складністю, наскільки ви найкраще можете і відчуєте себе в змозі.


18

Розглянемо

// myHeader.h
#include <sstream>
using namespace std;


// someoneElses.cpp/h
#include "myHeader.h"

class stringstream {  // Uh oh
};

Зауважте, що це простий приклад. Якщо у вас є файли з 20 включеннями та іншим імпортом, вам доведеться пройти тонну залежностей, щоб вирішити проблему. Гірше в тому, що ви можете отримати непов'язані помилки в інших модулях залежно від визначень, які конфліктують.

Це не жахливо, але ви вбережете себе від головного болю, не використовуючи його у файлах заголовків чи глобальному просторі імен. Це, мабуть, добре, робити це в дуже обмеженій області застосування, але мені ніколи не було проблеми вводити зайві п’ять символів, щоб уточнити, звідки беруться мої функції.


18
  1. Потрібно вміти читати код, написаний людьми, які мають інший стиль та думку найкращих практик, ніж ви.

  2. Якщо ви тільки використовуєте cout, ніхто не плутається. Але коли у вас багато просторів імен, які летять навколо, і ви бачите цей клас, і ви не точно впевнені, що він робить, якщо простір імен явно виступає в ролі коментарів. Ви можете побачити з першого погляду "о, це операція з файловою системою" або "це робить мережеві речі".


17

Використання багатьох просторів імен одночасно, очевидно, є рецептом катастрофи, але використання простого імен JUST stdта лише простору іменstd на мою думку, не є великою справою, тому що перевизначення може відбуватися лише за допомогою власного коду ...

Тому просто розгляньте їх як зарезервовані імена, такі як "int" або "class", і це все.

Люди повинні перестати бути настільки анальними щодо цього. Ваш вчитель весь час був прав. Просто використовуйте ОДНІ простір імен; в цьому і полягає вся суть використання просторів імен на першому місці. Ви не повинні одночасно використовувати більше одного. Якщо тільки це не ваше власне. Тож, повторне визначення не відбудеться.


Створення зіткнень - це не так складно - короткі рядки, як min, endі lessз'являються в std::просторі імен. Але більше, тепер, коли в ньому std::є тисячі символів, читачеві корисно знати, звідки походить новий символ, який він може не знати.
Том Свірлі

Простір імен std існує тому, що люди, або ви, ваші колеги, або люди, які пишуть проміжне програмне забезпечення, яке ви використовуєте, не завжди розумні щодо розміщення функцій всередині просторів імен. Таким чином, ви можете імпортувати всі std :: та нічого іншого, при цьому викликуючи зіткнення, скажімо, std :: min та чужої спадщини :: min () з часу, коли воно було в std.
Ейкен Барабан

14

Я погоджуюся з іншими тут, але я хотів би вирішити питання щодо читабельності - уникнути всього цього можна, просто використовуючи typedefs у верхній частині файлу, функції або декларації класу.

Зазвичай я використовую це у своїй декларації класу, оскільки методи класу, як правило, мають справу з подібними типами даних (членами), а typedef - це можливість призначити ім'я, яке має сенс у контексті класу. Це фактично сприяє читабельності у визначеннях методів класу.

// Header
class File
{
   typedef std::vector<std::string> Lines;
   Lines ReadLines();
}

і в реалізації:

// .cpp
Lines File::ReadLines()
{
    Lines lines;
    // Get them...
    return lines;
}

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

// .cpp
vector<string> File::ReadLines()
{
    vector<string> lines;
    // Get them...
    return lines;
}

або:

// .cpp
std::vector<std::string> File::ReadLines()
{
    std::vector<std::string> lines;
    // Get them...
    return lines;
}

Лише незначний коментар, хоча typedef корисний, я б розглядав можливість створення класу, який представляє Lines замість використання typedef.
Еял Сольник

14

Конкретний приклад для з’ясування проблеми. Уявіть, у вас ситуація, коли у вас є дві бібліотеки, fooі barкожна з їх власним простором імен:

namespace foo {
    void a(float) { /* Does something */ }
}

namespace bar {
    ...
}

Тепер припустимо, що ви використовуєте , fooі barразом у вашій власній програмі наступним чином :

using namespace foo;
using namespace bar;

void main() {
    a(42);
}

У цей момент все добре. Коли ви запускаєте свою програму, вона робить щось. Але пізніше ви оновите barі скажімо, що це змінилося так:

namespace bar {
    void a(float) { /* Does something completely different */ }
}

У цей момент ви отримаєте помилку компілятора:

using namespace foo;
using namespace bar;

void main() {
    a(42);  // error: call to 'a' is ambiguous, should be foo::a(42)
}

Тож вам потрібно буде провести певне обслуговування, щоб уточнити, що означає «а» foo::a. Це небажано, але, на щастя, це досить просто (просто додайте foo::перед усіма дзвінками, aщо компілятор відзначає як неоднозначне).

Але уявіть альтернативний сценарій, коли замість цього рядка змінився, щоб виглядати замість цього:

namespace bar {
    void a(int) { /* Does something completely different */ }
}

У цей момент ваш заклик a(42)раптово пов'язується із bar::aзамість цього, foo::aа замість того, щоб робити щось, він робить щось зовсім інше. Ніякого попередження компілятора чи нічого. Ваша програма просто мовчки починає робити щось по-іншому, ніж раніше.

Використовуючи простір імен, ви ризикуєте подібним сценарієм, тому людям незручно користуватися просторами імен. Чим більше речей у просторі імен, тим більший ризик виникнення конфлікту, тому людям може бути навіть незручніше використовувати простір імен std(через кількість речей у цьому просторі імен), ніж інші простори імен.

Зрештою, це компроміс між записуваністю та надійністю / ремонтопридатністю. Читання також може вплинути на це, але я міг бачити аргументи для цього в будь-якому випадку. Як правило, я б сказав, що надійність та ремонтопридатність важливіші, але в цьому випадку ви постійно будете платити витрати на справність за досить рідкісний вплив на надійність / ремонтопридатність. Найкраща компромісна пропозиція визначатиме ваш проект та ваші пріоритети.


Другий сценарій стосується угоди для мене. Знову немає просторів імен. Неможливо, щоб такі витончені зміни функціональності залишалися непоміченими під кришкою.
safe_malloc

13

Простір імен - це названа область. Простори імен використовуються для групування пов'язаних декларацій та для збереження окремих елементів. Наприклад, дві окремо розроблені бібліотеки можуть використовувати одне ім’я для позначення різних елементів, але користувач все ще може використовувати обидва:

namespace Mylib{
    template<class T> class Stack{ /* ... */ };
    // ...
}

namespace Yourlib{
    class Stack{ /* ... */ };
    // ...
}

void f(int max) {
    Mylib::Stack<int> s1(max); // Use my stack
    Yourlib::Stack    s2(max); // Use your stack
    // ...
}

Повторення імені простору імен може відволікати увагу як для читачів, так і для письменників. Отже, можна констатувати, що імена з певного простору імен доступні без явної кваліфікації. Наприклад:

void f(int max) {
    using namespace Mylib; // Make names from Mylib accessible
    Stack<int> s1(max); // Use my stack
    Yourlib::Stack s2(max); // Use your stack
    // ...
}

Простори імен - це потужний інструмент для управління різними бібліотеками та різними версіями коду. Зокрема, вони пропонують програмісту альтернативи того, як явно зробити посилання на нелокальне ім’я.

Джерело: Огляд мови програмування C ++ від Bjarne Stroustrup


4
Дуже цікаво, що ця відповідь, що базується на вказівках інших людей, що Б'ярн Струструп заробив -2 ... хлопчик Б'ярн, мабуть, був бідним і недосвідченим програмістом, коли ввів цю функцію в C ++
nyholku

@nyholku: Дивіться це .
sbi

10

Приклад, коли using namespace stdкидається помилка компіляції через неоднозначність підрахунку, що також є функцією в бібліотеці алгоритмів.

#include <iostream>

using namespace std;

int count = 1;
int main() {
    cout << count << endl;
}

2
::count--проблема вирішена. Зазвичай у вас буде більше речей з простору імен std, ніж з інших місць, ерго, зберігаючи директиву щодо простору імен, може врятувати вас від набору тексту.
PSkocik

Справжня проблема тут полягає в тому, що C ++ все ще має глобальні простори, не мають простору імен. Це, і той факт, що "це" неявно в методах, викликає так багато помилок і проблем, що я навіть не можу їх порахувати, навіть при правильній змінній "count". ;)
Ейкен Барабан

9

Це не погіршує продуктивність вашого програмного забезпечення чи проекту. Включення простору імен на початку вашого вихідного коду непогано. Включення using namespace stdінструкції залежить від ваших потреб та способу розробки програмного забезпечення чи проекту.

namespace stdМістить C ++ стандартні функції і змінні. Цей простір імен корисний, коли ви часто використовуєте стандартні функції C ++.

Як згадується на цій сторінці :

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

І дивіться цю думку :

Немає проблем із використанням "std namespace std" у вихідному файлі, коли ви широко використовуєте простір імен і точно знаєте, що нічого не зіткнеться.

Деякі люди сказали, що це неправильна практика включати до using namespace stdсвоїх вихідних файлів, оскільки ви використовуєте з цього простору імен всі функції та змінні. Коли ви хочете визначити нову функцію з тим же ім'ям, що й іншу функцію, що міститься у namespace stdвас, ви перевантажите функцію, і вона може створювати проблеми через компіляцію чи виконання. Він не буде компілюватися чи виконуватись так, як ви очікували.

Як згадується на цій сторінці :

Хоча вислів рятує нас від введення std ::, коли ми хочемо отримати клас або тип, визначений у просторі імен std, він імпортує всю область простору імен std у поточний простір імен програми. Візьмемо кілька прикладів, щоб зрозуміти, чому це може бути не так добре

...

Тепер на більш пізньому етапі розробки ми хочемо використати іншу версію cout, яка спеціально реалізується в деякій бібліотеці під назвою “foo” (наприклад)

...

Зауважте, як існує двозначність, на яку бібліотеку вказує cout? Компілятор може виявити це, а не компілювати програму. У гіршому випадку програма все ж може компілювати, але викликати неправильну функцію, оскільки ми ніколи не вказували, до якого простору імен належав ідентифікатор.


8

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


7

"Чому" використовується простір імен std; " вважається поганою практикою в C ++? "

Я кажу про зворотне: Чому введення п'яти зайвих символів деякими вважається громіздким?

Розглянемо, наприклад, написання фрагмента програмного забезпечення для числення. Чому я б навіть міркував забруднювати глобальний простір імен, скорочуючи загальне "std :: vector" до "вектора", коли "вектор" є однією з найважливіших концепцій домену?


19
Це не просто 5 додаткових символів; 5 додаткових символів кожного разу, коли ви посилаєтесь на будь-який тип об'єкта в стандартній бібліотеці. Що, якщо ти дуже використовуєш стандартну бібліотеку, буде часто. Тож реально тисячі додаткових символів у програмі пристойного розміру. Імовірно, до мови була додана директива "використання", щоб її можна було використовувати ...
Джеремі Фріснер

5
Це не 5 додаткових символів кожного разу, це 5 символів, і, ймовірно, пара клацання мишкою, щоб знищити меню і зробити пошук і заміну в редакторі на ваш вибір.
DaveWalley

1
Читабельність. cout << hex << setw(4) << i << endl;легше читати, ніжstd::cout << std::hex << std::setw(4) << i << std::endl;
oz1cz

16
А ще гірше: std::map<std::string,std::pair<std::string,std::string>>жахливо порівняно з map<string,pair<string,string>>.
oz1cz

4
Хороша практика - все одно набирати ваші контейнери STL, тому std :: там насправді не має значення. І C ++ 11 приніс нам ключове слово авто, що робить речі ще простішими, наприклад, за допомогою ітераторів.
джузлін

7

Я погоджуюся з іншими - це вимагає сутичок з іменами, неоднозначностей, і тоді факт є менш явним. Хоча я бачу використання using, мої особисті переваги - обмежити його. Я також настійно розглядаю те, на що вказували деякі інші:

Якщо ви хочете знайти ім'я функції, яке може бути досить поширеним іменем, але ви хочете знайти його лише в stdпросторі імен (або в зворотному - ви хочете змінити всі виклики, які не є в просторі імен std, просторі імен X, ...), то як ви пропонуєте це зробити?

Ви можете написати програму, щоб це зробити, але чи не було б краще витратити час на роботу над самим проектом, а не на написання програми для підтримки свого проекту?

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

Є ще одна річ, хоча вона дещо пов’язана з вищезазначеним і тим, що вказують інші. Хоча це може бути поганою практикою, я іноді зарезервую std::nameстандартну версію бібліотеки та ім'я для конкретної програми. Так, справді це могло б вас вкусити і сильно вкусити, але все зводиться до того, що я розпочав цей проект з нуля, і я єдиний програміст для нього. Приклад: Я перевантажую std::stringі називаю це string. У мене є корисні доповнення. Я зробив це частково через свою схильність C і Unix (+ Linux) до малих імен.

Крім того, ви можете мати псевдоніми простору імен. Ось приклад того, де це корисно, про що, можливо, не згадувалося. Я використовую стандарт C ++ 11 і конкретно з libstdc ++. Ну, у нього немає повної std::regexпідтримки. Звичайно, він компілюється, але він викидає виняток у такий спосіб, що є помилкою в кінці програміста. Але це недостатня реалізація.

Отже ось як я це вирішив. Встановіть регекс Boost і зв’яжіть його. Потім я виконую наступне, щоб, коли libstdc ++ повністю реалізований, мені потрібно лише видалити цей блок, і код залишається тим самим:

namespace std
{
    using boost::regex;
    using boost::regex_error;
    using boost::regex_replace;
    using boost::regex_search;
    using boost::regex_match;
    using boost::smatch;
    namespace regex_constants = boost::regex_constants;
}

Я не буду сперечатися, це погана ідея чи ні. Однак я заперечую, що це забезпечує чистоту для мого проекту, і в той же час робить його конкретним: правда, я повинен використовувати Boost, але я використовую його як libstdc ++, зрештою, це буде. Так, починаючи власний проект і починаючи зі стандарту (...) на самому початку йде дуже довгий шлях, допомагаючи технічному обслуговуванню, розробці та всьому, що пов'язано з проектом!

Просто для уточнення чогось: я насправді не думаю, що вдало використовувати назву класу / що завгодно в STL навмисно та конкретніше замість. Рядок є винятком (ігноруйте перше, вище, або друге тут, каламбур), оскільки мені не сподобалася ідея "String".

Як це є, я все ще дуже упереджений до С та упереджений щодо С ++. Економні деталі, багато чого з того, над чим я працюю, підходить більше C (але це була хороша вправа і хороший спосіб зробити так, щоб я засвоїв іншу мову і б. Спробуйте не бути менш упередженим щодо об'єкта / класів / тощо, що, можливо, краще заявляється як менш замкнуті, менш зарозумілі та більше сприймають.). Але що є корисним є те , що деякі з них вже запропонували: Я дійсно використовувати список (це досить загальний характер , чи не так?), І виду ( то ж саме) , щоб назвати два , що викликало б конфлікт імен , якби я зробити using namespace std;, і так з цією метою я віддаю перевагу конкретному контролю і знаю, що якщо я маю намір це бути стандартним використанням, то мені доведеться вказати це. Простіше кажучи: припущення не допускається.

А щодо того, щоб зробити регекс Boost частиною std. Я роблю це для подальшої інтеграції і - знову ж таки, повністю визнаю, це упередженість - я не думаю, що це так негарно boost::regex:: .... Дійсно, це інша річ для мене. У C ++ є багато речей, які мені ще належить до кінця прийняти у зовнішньому вигляді та методах (інший приклад: варіативні шаблони та аргументи var (хоча я визнаю, що варіативні шаблони дуже корисні!)). Навіть ті, що я приймаю, були важкими, і у мене все ще є проблеми з ними.



7

З мого досвіду, якщо у вас є кілька бібліотек, які використовують, скажімо cout, але для іншої мети ви можете використовувати неправильно cout.

Наприклад, якщо я друкую, using namespace std;і using namespace otherlib;та типу тільки cout(що трапляється в обох), а не std::cout(або 'otherlib::cout'), ви можете використовувати неправильний, і отримати помилки. Це набагато ефективніше і ефективніше використовувати std::cout.


6

Для некваліфікованих імпортованих ідентифікаторів вам потрібні зовнішні інструменти пошуку, такі як grep, щоб дізнатися, де оголошено ідентифікатори. Це ускладнює міркування щодо правильності програми.


6

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


6

Це погана практика, часто відома як глобальне забруднення простору імен. Проблеми можуть виникнути, коли більше ніж один простір імен має однакове ім’я функції з підписом, тоді компілятор буде неоднозначно вирішувати, кому слід зателефонувати, і цього всього можна уникнути, коли ви визначаєте простір імен з таким викликом функції, як std::cout. Сподіваюсь, це допомагає. :)


5

Щоб відповісти на ваше запитання, я розглядаю це таким чином практично: багато програмістів (не всі) викликають простір імен std. Тому слід звичати НЕ використовувати речі, які заважають або використовують ті самі назви, що і в просторі імен std. Це велика справа, але не стільки в порівнянні з кількістю можливих зв’язних слів і псевдонімів, які можна придумати строго.

Я маю на увазі дійсно ... сказати "не покладайсь на це, що є присутнім" - це просто налаштувати тебе покладатися на те, що НЕ присутній. Ви постійно будете мати проблеми, запозичуючи фрагменти коду та постійно їх ремонтуючи. Просто тримайте визначені користувачем і запозичені речі обмеженим обсягом, як вони мають бути, і ДУЖЕ порівнюйте їх із глобальними (чесно кажучи, глобальні майже завжди повинні бути крайнім засобом для цілей "скласти зараз, розумніше пізніше"). Дійсно, я вважаю, що це погана порада вашого вчителя, оскільки використання std буде працювати як для "cout", так і для "std :: cout", але НЕ використання std буде працювати тільки для "std :: cout". Вам не завжди пощастить написати весь власний код.

ПРИМІТКА: Не зосереджуйтеся занадто багато на питаннях ефективності, поки ви фактично не дізнаєтесь трохи про те, як працюють компілятори. Маючи невеликий досвід кодування, вам не доведеться дізнаватися багато про них, перш ніж зрозуміти, наскільки вони здатні узагальнити хороший код у щось щось просте. Кожен шматочок настільки ж простий, як якщо б ви написали всю справу в C. Хороший код настільки ж складний, як і повинен бути.


Зважаючи на те, скільки людей здається невідомими корисних стандартних функцій бібліотеки (наприклад, винаходити речі <algorithm>, наприклад), здається, що уявити, що ті самі люди могли надійно уникнути цих ідентифікаторів. Подивіться власний код і скажіть мені, що ви ніколи не називаєте змінну чи функцію count. Або distance, або log, destroy, launch, visit, beta, sample, messages, clamp, erase, copy, modulus, leftі т.д. Не кажучи вже про всіх ідентифікаторах ще не в stdтому , що порушить ваш код , коли C ++ 35 виходить ...
Тобі Спейт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.