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


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

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


2069
2017-09-21 03:08


походження


Не забудьте, що ви можете зробити: "Використовуючи std :: cout;" що означає, що вам не потрібно вводити std :: cout, але не введіть весь простір імен std одночасно. - Bill
@a платив німець google-styleguide.googlecode.com/svn/trunk/... посилання більше не працює. Схоже, нове посилання є google.github.io/styleguide/cppguide.html#Other_C++_Features - MCG
Особливо погано використовувати "використання простору імен" у області файлу у файлах заголовків. Використання його в початкових файлах (* .cpp) у файловій області після того, як все включено, не так вже й погано, оскільки його ефект обмежується одиничним одиницею перекладу. Навіть менш проблематичним є використання його всередині функцій або класів, оскільки його ефект обмежується простором функції або класу. - sh-
Я б не рекомендував використовувати директиву, але для конкретних областей імен, як std::literals::chrono_literals, Poco::Data:Keywords,Poco::Units і речі, які будуть мати справу з літералами або трюки для читання. Кожного разу, коли він знаходиться в заголовку або файлах реалізації. Можливо, в порядку функціонування, я думаю, це добре, але крім літер та інших речей це не є корисним. - Ludovic Zenohate Lagouardette
@Jon: Це не має нічого спільного з простором імен namespace зокрема. Мій акцент був спрямований на "на файловій області в файлах заголовків". Поставити це як пораду: не використовуйте "використання простору імен" (std або інше) у області файлу у заголовках файлів. Добре використовувати його у файлах реалізації. Вибачте за невизначеність. - sh-


Відповіді:


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

using namespace foo;
using namespace bar;

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

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


1751
2017-09-21 03:13



Мені завжди сподобалося, що Python "імпортує big_honkin_name як bhn", тому ви можете просто використовувати "bhn.something", а не "big_honkin_name.something" - дійсно скорочує типізацію. Чи має C ++ щось подібне? - paxdiablo
@Pax namespace io = boost :: файлова система; - AraK
Я думаю, це перебільшення речей, щоб сказати, що це "певні зусилля, щоб виправити". Ви не матимете жодних прикладів нового foo :: Quux, так що просто зніміть всі ваші поточні використання з панеллю :: Quux. - MattyT
Чи будь-яка розсудлива людина створить бібліотеку з типом, чию некваліфіковане ім'я зіштовхується зі типами std? - erikkallen
@TomA: проблема з #define полягає в тому, що він не обмежує себе просторами імен, але перетворюється на всю кодову базу. Псевдонім імен є те, що ви хочете. - sbi


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

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

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

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


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

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


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


1152
2017-09-21 09:26



Це робить значний збиток для щільності коду, який ви можете упакувати в одній лінії. Ви в кінцевому підсумку пишете свій код дуже довгостроково; що зменшує читаність. Особисто я думаю, що коротший (але не дуже короткий) код, як правило, більш читабельний (оскільки менше матеріалу для читання, і менше матеріалу, щоб відвлекатися). - Lie Ryan
Здогадайтеся, що ви пропустили старі часи, перш ніж C ++ мав стандарт string клас, і, здавалося б, кожна бібліотека мала власну. Розкажіть вам: ми продовжуватимемо писати наш код std::, і ви можете запустити наш код через grep -v std:: | vim коли ви переглядаєте це. Або ви можете навчити свого редактора, що std:: це ключове слово, яке буде забарвлено таким же, як фоновий колір. Що б не було. - Mike DeSimone
Я не думаю std:: взагалі шкідливий. Вона містить дуже важливу інформацію (а саме: "що завгодно після цього є частиною стандартної бібліотеки", і це все ще досить короткий і компактний префікс. Більшість часу це не проблема. Іноді у вас є кілька рядків коду де вам потрібно послатися на певні символи в std багато імен, а потім а using Заява в цій конкретній області вирішує проблему красиво. Але в загальному випадку це не шум, він передає цінні відомості в додаток для усунення неоднозначності. - jalf
Кожного разу, коли я бачу std::, Я знаю, що це буде std:: не маючи на це думати. Якщо я бачу string або list або map самі по собі, мені цікаво трохи. - Mateen Ulhaq
@LieRyan Тоді удачі написання геометрію бібліотеки, ніколи не називаючи щось vector, transform або distance. І це лише приклади багато багатьох дуже поширених імен використовується в стандартній бібліотеці. Пропонуйте не використовувати їх у страху або упереджене уявлення про функцію простору імен, яка є невід'ємною частиною C ++, є досить протилежною. - Christian Rau


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

Тим не менш, ви можете вільно розміщувати твердження з використанням у ваших (приватних) * .cpp файлах.


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

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

Це пропонує два варіанти:

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

    using std::cout; // a using-declaration lets you use cout without qualification
    cout << "Values:";
    
  • Переходьте і просто введіть std ::

    std::cout << "Values:";
    

309
2017-09-21 03:22



Менш складно помістити його в файл .cpp, ніж у заголовку, але проблема залишається незмінною для ремонтопридатності. Він ризикує зіткнутися двох функцій з тим самим ім'ям, коли код / ​​бібліотека, яку ви використовуєте, або стандарт C ++ змінюється. - Étienne
Ви пробували це взагалі? Тому що директива про використання простору імен не буде перенесена в інший файл. (GCC 4.8+) - zackery.fix
@ zackery.fix, "Apple LLVM версії 7.0.2 (clang-700.1.81)" поширюється using namespace std; від заголовка до вихідного файлу, і я перевірив, що GCC не робить. Тому, принаймні, директива "Використання" в заголовку є ризикованою. - Franklin Yu
Yea .. Я не використовую LLVM або clang, і це не стандартний підхід у будь-якому випадку. - zackery.fix
Ви дійсно не повинні говорити людям про "відчуття вільності" за допомогою простору імен std у файлі .cpp. @ Етьен правий. - einpoklum


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

using namespace std;
using namespace boost;

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

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


197
2017-10-28 17:37



Це. Boost і std мають багато перекриття - особливо з C + + 11. - einpoklum
Я зробив це одного разу і навчився уроку важким шляхом. Тепер я ніколи не користуюсь using за межами визначення функції і рідко використовують using namespace зовсім. - Ferruccio


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

Резюме

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

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

Обговорення

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


159
2017-11-03 20:00



Сумно, що це нормальне керівництво так поховано під помилковими відповідями. - nyholku
Ще одна думка програміста тут, але поки я згоден на 100% з твердженням, що це слово using ніколи не повинно з'являтися в заголовку, я не так переконаний у вільній ліцензії на місце using namespace xyz; в будь-якому місці вашого коду, особливо якщо xyz є std. Я використовую using std::vector; формі, оскільки це лише витягує один елемент із простору імен у псевдо-глобальний масштаб, що веде до значно меншого ризику зіткнення. - dgnuff
@ Lighting Races на орбіті ви, звичайно, маєте право на вашу думку. Було б більш корисним, якщо була спроба пояснити, чому ви не згодні з порадами, наведеними у цьому відповіді. Особливо було б цікаво зрозуміти, що таке точка просторів імен, якщо їх використання "погано"? Чому б не просто назвати речі std_cout, а не std :: cout ... творці C ++ / namespace мали деяку ідею, коли їм було важко створити їх. - nyholku
@nyholku: Немає необхідності - більшість інших відповідей дають ті самі причини, якими я б хотів. Також, будь ласка, не соромтеся відзначити ":)", який я додав до мого коментарю! І це не означає, що простості імен погані. - Lightness Races in Orbit
Так, я помітив, що :) але IMO більшість відповідей (які йдуть проти цього мудреця поради) помилкові (не те, що я зробив статистику, яка більшість зараз). Якщо ви погоджуєтеся, що простір імен є "непоганим", то ви можете сказати, де ви вважаєте, що вони є доречними, якщо ви не погоджуєтеся з цією відповіддю? - nyholku


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

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...) тому що він коротший і здатний працювати з визначеними користувачем типами з плаваючою точкою (за допомогою аргументного залежності).


103
2017-09-21 15:47



Мені шкода, але я сильно не погоджуюсь з цим. - Billy ONeal
@Billy: Інший спосіб підтримувати виклик userlib :: cos (userlib :: superint) не існує. Кожна функція використовується. - Zan Lynx
@Зан: Звичайно, є. using std::cos; , using std::sinі т. д. Проблема хоча і полягає в тому, що вона добре продумана userlib збирається мати їх sin і cos всередині власного простору імен, так що це дійсно не допомагає вам. (Якщо там немає using namespace userlib перед цим шаблоном, і це так само погано, як using namespace std - і сфера там не обмежена.) Крім того, єдиною функцією, як це я коли-небудь бачу це сталося swap, і в таких випадках я б рекомендував просто створити шаблон спеціалізації std::swap і уникаючи всієї проблеми. - Billy ONeal
@BillyONeal: template<typename T> void swap(MyContainer<T>&, MyContainer<T>&) (Немає часткової спеціалізації шаблону функцій (FTPS), тому іноді вам потрібно скористатися перевантаженням. - sbi
@BillyONeal: Ваш коментар (7 разів перевищено!) Неправильний - ситуація, яку ви описуєте точно який ADL був розроблений для покриття. Коротко, якщо x має один або декілька "пов'язаних областей імен" (наприклад, якщо вони були визначені в namespace userlib), то будь-яка виклик функції виглядає cos(x) воля додатково дивись у цих просторах імен - без будь-який using namespace userlib; заздалегідь необхідність. Zan Lynx правильно (і пошук по імені C + + є візантіним ...) - j_random_hacker


Не використовуйте його глобально

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

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

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

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

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

У C ++ 03 існував ідіом - коментувати код - для реалізації a 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 було змінено, щоб знайти потенційну перевантаження та вибрати його.


80
2018-01-18 09:34



"Реалізація std :: swap була змінена, щоб знайти потенційну перевантаження та вибрати її". - Що? Ви впевнені в цьому? Хоча це правда, що надання звичай swap на першому місці не так вже й важливо в C ++ 11, оскільки std::swap сама по собі є більш гнучкою (використовує переміщення семантики). Але std::swap автоматично вибравши власний індивідуальний своп, це абсолютно нове для мене (і я дійсно не вірю в це). - Christian Rau
@ ChristianRau Я думаю так, так. Я десь читав це на СО. Ми завжди можемо попросити Говард, він повинен знати. я є копання і копання зараз ... - towi
Навіть у випадку обміну, більш чітка (і, на щастя, більш поширеною) ідіома - це писати using std::swap;а не using namespace std;. Більш конкретна ідіома має меншу кількість побічних ефектів і тому робить код більш ретельним. - Adrian McCarthy
Остання фраза неправильна. У C ++ 11 в Std обмін двома кроками був офіційно благословенний як правильно спосіб зателефонувати swap, і різні інші місця в стандарті були змінені, щоб сказати, що вони закликають swap як це (N.B. як зазначено вище, using std::swap це правильний шлях, а не using namespace std) Але std::swap сам був рішуче ні змінилося, щоб знайти інше swap і використовувати його. Якщо std::swap називається, потім std::swap звикнеш - Jonathan Wakely
Це може бути мудріше просто набрати using std::swap локально, однак, щоб зменшити локальне простір імен, одночасно створюючи код самодокументації. Ви рідко зацікавлені в загальному простору імен std, тому просто знайдіть ті частини, які вас цікавлять. - Lundin


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

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


71
2017-09-21 03:23



+1 не кажучи вже про це distance. все-таки я віддаю перевагу неквалифікованим іменам, якщо це практично неможливо, оскільки це збільшує читаність для мене. Крім того, я думаю, що той факт, що ми зазвичай не кваліфікуємо речі у усній мові та готові витрачати час на вирішення можливих неоднозначностей, означає, що він має цінність, щоб вміти зрозуміти, про що мова йде без кваліфікацій, і застосовується до джерела код, що означає, що він структурований таким чином, що зрозуміло, що це все навіть без кваліфікації. - Cheers and hth. - Alf
Щоб бути справедливим, у вас немає більшості з тих, хто не включений <iomanip>. Все-таки хороший момент. - einpoklum


Я погоджуюся, що його не слід використовувати в глобальному масштабі, але це не так зло, щоб використовувати місцево, як у 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)


34
2017-08-29 09:44





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

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

Що це вагомі причини? Іноді програмісти явно хочуть вимкнути ADL, інакше вони хочуть, щоб вони не збігалися.

Отже, ось так:

  1. Функціональний рівень використання директив та використання-декларації всередині реалізацій функцій
  2. Використання-декларації у вихідному файлі на рівні вихідного файлу
  3. (Іноді) використання директив із використанням source-file-level

34
2018-03-29 08:10



Визначте "елегантність". - sbi