Що таке "використання простору імен" забруднення?


15

Я дивився посібник з кодування Google [тут], і вони не рекомендують користуватися using namespaceабо namespace::function- якщо я не неправильно трактував це.

Чи стосується це stdтакож? cout<<без нього не працює. Ця книга , рекомендує те саме. Тож як я можу користуватися cout<<без using namespace std;або std::cout<<?

Який рекомендований спосіб? std::cout<<? Більшість підручників c ++ навчають новачків, using namespace std;чи вони поширюють погану практику кодування?

Відповіді:


18

Коли я читав стандарт Google, ви не можете використовувати using namespace foo;директиву ніде. Ця директива містить у собі все, що оголошено в просторі імен, і є частою причиною зіткнень та несподіваної поведінки. Інші цитують дуже поширений: у вас є десь свій метод max або min, і він стикається у src-файлі, де хтось включає заголовк із вашим методом, а потім кажеusing namespace std;

У певних місцях дозволяється мати користувальну декларацію, яка є формою using ::foo::bar;

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

#include <ostream>
//using namespace std; // NO!
//using ::std::cout;   // less bad than using namespace, but I prefer to scope it

int main(int argc, char** argv)
{
   int rc = do_some_stuff(argc, argv);
   using ::std::endl;
   if (rc) { // print the success report
      using ::std::cout;
      cout << "The test run completed. The return code was " << rc << '.' << endl;
    } else {
      using ::std::cerr;
      cerr << "Unable to complete the test run." << endl;
    }
    return 0 == rc;
}

Це невелика екстремальність, коли лише кілька ліній роблять вихід, але ви отримуєте ідею.

Ще одне, що можна зробити, це псевдонім або typedef, щоб мінімізувати введення тексту. Я не знаходжу std :: як би це не було погано, але у нас є величезний набір джерела з декількома десятками модулів, і іноді нам доводиться писати подібний код console_gui::command_window::append("text"). Це стає нудним через деякий час і викликає багато довгих ліній. Я все за щось подібне

typedef console_gui::command_window cw;
cw::append("text");

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


1
Спасибі! Це справді корисно. Ви не тільки пояснили, чому це погано з приємними прикладами, але і вказали на рішення з приємними прикладами. :-)
Лорд Лох.

1
BTW: Використання std::endlдля явного потоку увімкнено stdout/, stderrяк правило, зовсім зайве, ці потоки прив'язані до stdout/ у stderrбудь-якому випадку. Це навіть трохи уповільнює справи.
Дедуплікатор

8

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

Наприклад, якщо ви включите та визначите власну функцію max (), вона зіткнеться з std :: max ().

http://en.cppreference.com/w/cpp/algorithm/max

Перевагою є використання std :: member_you_wish_to_use, оскільки в ньому прямо вказано, який простір імен використовувати.


Я припускаю, що це означає, що я повинен використовувати std::max()з префіксом простору імен. Або я помиляюся?
Лорд Лох.

3
Це означає, що якщо ви поставите "з використанням простору імен std;" у вашому коді ви отримаєте помилки, якщо визначите власну функцію max (або будь-яке інше ім’я, яке вже визначено в просторі імен std)
Chewy Gumball,

1
Це просто означає, що вам слід бути обережними з usingдирективами, оскільки в цьому випадку вона порушить вашу функцію max (), якби ви визначили її і включили <алгоритм>. Це простий випадок, але ви ніколи не знаєте, що ви можете зламати. Вам потрібно буде знати всю бібліотеку, щоб бути впевненою, що ви її не зламали, але не можете знати, чи порушиться ваш код (тобто зіткнення імені) у майбутньому.
ApplePie

6

Цитуючи надане вами посилання:

Ви можете використовувати декларацію з використанням де-небудь у файлі .cc та у функціях, методах чи класах у .h-файлах.

// ОК у файлах .cc.

// Повинно бути у функції, методі чи класі у .h файлах.

використовуючи :: foo :: bar;

Стиль Google забороняє вам імпортувати простори імен у глобальному контексті, але дозволяє це робити у локальних.

Всюди, де використання декларації впливає лише на обмежену і чітко видиму частину коду, це цілком прийнятно.

Коли ви забруднюєте глобальний контекст, це впливає на незв'язаний код (імпліцит за допомогою заголовка). Нічого не відбувається, коли ви робите це в локальному контексті.


У нас однакові стандарти. Деякі наші люди локально наберуть довгий простір імен. наприклад, typedef дурний :: barlicious fb; fb :: пити d;
Майкл Метюз

1

вони не рекомендують використовувати простір імен orpacepace names: function` - якщо я його неправильно не витлумачив.

Ти зробив. Дирекомендація стосується лише using namespaceдирективи (яку зазвичай називають abusing namespaceне зовсім гумористично). Настійно бажано використовувати повністю кваліфіковане ім’я функції або об'єкта, наприклад std::cout.


1

Хоча на запитання вже є корисні відповіді, одна деталь, здається, є занадто короткою.

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

Ідентифікатори в просторах імен доступні, чітко називаючи простір імен:

myNamespace::myIdent

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

... звичайно, є різниця між використанням декларації

using myNamespace::myIdent; // make myIdent an alias of myNamespace::myIdent

і використовуючи директиву

using myNamespace; // make all identifiers of myNamespace directly accessible

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


1

Ось вам:

#include <iostream>

int main()
{
    std::endl(std::operator<<(std::cout, "Hello world!"));
}

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

Це мається на увазі саркастична відповідь. :-D

Я разом з Герб Саттером над Google. З стандартів кодування C ++:

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

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

Більшість підручників c ++ навчають початківців користуватися простором імен std; чи вони поширюють погану практику кодування?

Якщо ви запитаєте мене, навпаки, і я вважаю, що Саттер вище погоджується.

Тепер, протягом своєї кар'єри, я зіткнувся з приблизно 3 конфліктами у просторі імен, як прямий результат usingдиректив у кодових базах, що охоплюють десятки мільйонів LOC. Однак у всіх 3 випадках вони знаходились у вихідних файлах, що налічували понад 50 000 рядків спадкового коду, спочатку написаних на C, а потім утисків на C ++, виконуючи величезний еклектичний список розрізнених функцій, включаючи заголовки з десятка різних бібліотек, і епічний список, #includesякий охоплював сторінку. Незважаючи на епічний безлад, виправити їх було не так складно, оскільки вони спричинили помилки збирання в OSX (тієї ОС, де код не вдалося створити), а не помилок виконання. Не організовуйте свій код таким кошмарним способом, і вам слід добре.

Однак, уникайте обох using директив та декларацій у файлах заголовків. Це просто затримка. Але для вихідних файлів, особливо тих, на яких ціла сторінка не заповнена #includeдирективами, я б сказав, що не переглядайте її, якщо ви не працюєте в Google.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.