Почему «используется пространство имен std;» считается плохой практикой?

2642

Другие говорили мне, что писать using namespace std;в коде неправильно, и что я должен использовать std::coutи std::cinнепосредственно вместо этого.

Почему using namespace std;считается плохой практикой? Это неэффективно или существует риск объявления неоднозначных переменных (переменных, которые имеют то же имя, что и функция в stdпространстве имен)? Влияет ли это на производительность?

akbiggs
источник
513
Не забудьте, что вы можете сделать: «используя 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) в области действия файлов после всех включений не так уж плохо, поскольку его эффект ограничен одной единицей перевода. Еще менее проблематично использовать его внутри функций или классов, потому что его эффект ограничен областью действия функции или класса.
sh-
5
Я бы обескуражить использовать директиву , но для определенных пространств имен нравятся std::literals::chrono_literals, Poco::Data:Keywords, Poco::Unitsи вещи , которые будут иметь дело с литералами или читаемостями трюками. Всякий раз, когда это находится в заголовке или файлах реализации. Я думаю, это может быть нормально в области функций, но кроме литералов и прочего, это бесполезно.
Людовик Зенохате Лагуардетт
7
@Jon: Это не имеет никакого отношения к пространству имен, в частности. Мой акцент был сделан на «в области видимости файлов заголовка». Чтобы сформулировать это как совет: не используйте «использование пространства имен» (std или другое) в области видимости файлов заголовочных файлов. Можно использовать его в файлах реализации. Извините за двусмысленность.
sh-

Ответы:

2232

Это никак не связано с производительностью. Но учтите это: вы используете две библиотеки с именами 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 "import big_honkin_name as bhn", так что вы можете просто использовать "bhn.something" вместо "big_honkin_name.something" - действительно сократить ввод текста. Есть ли в C ++ что-то подобное?
paxdiablo
764
@Pax namespace io = boost :: filesystem;
AraK
152
Я думаю, что это преувеличение, сказать, что это «какое-то усилие, чтобы исправить». У вас не будет экземпляров нового foo :: Quux, поэтому устраните неоднозначность всех ваших текущих применений с помощью bar :: Quux.
MattyT
289
Будет ли какой-нибудь разумный человек создать библиотеку с типами, чье неквалифицированное имя будет конфликтовать с типами std?
erikkallen
94
@TomA: Проблема в #defineтом, что он не ограничивается пространством имен, а попирает всю базу кода. Псевдоним пространства имен - это то, что вы хотите.
ВОО
1391

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

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

Имейте в виду , что stdпространство имен имеет тонн идентификаторов, многие из которых являются очень распространенными из них (думаю list, sort, string, iteratorи т.д.) , которые очень вероятно, появится в другом коде тоже.

Если вы считаете это маловероятным: здесь был задан вопрос о переполнении стека, где почти точно это произошло (неправильная функция вызвана из-за пропущенного std::префикса) примерно через полгода после того, как я дал этот ответ. Вот еще один, более свежий пример такого вопроса. Так что это настоящая проблема.


Вот еще один момент данных: много-много лет назад я также находил, что это раздражает необходимость префиксировать все из стандартной библиотеки с помощью std::. Затем я работал в проекте, где с самого начала было решено, что и usingдирективы, и объявления запрещены, за исключением областей действия функций. Угадай, что? Большинству из нас потребовалось несколько недель, чтобы привыкнуть к написанию префикса, и спустя еще несколько недель большинство из нас даже согласилось с тем, что это фактически делает код более читабельным . Для этого есть причина: нравится ли вам короткая или длинная проза, субъективно, но префиксы объективно добавляют ясности в код. Не только компилятору, но и вам легче понять, на какой идентификатор ссылаются.

За десятилетие этот проект вырос до нескольких миллионов строк кода. Поскольку эти обсуждения возникают снова и снова, мне однажды было любопытно, как часто (разрешенная) область действия функции usingфактически использовалась в проекте. Я нашел источники для него и нашел только одно или два десятка мест, где он использовался. Для меня это означает, что после std::попытки разработчики не находят достаточно болезненным применение директив даже один раз каждые 100 кЛо, даже там, где это разрешено использовать.


Итог: явный префикс всего не приносит никакого вреда, требует очень мало привыкания и имеет объективные преимущества. В частности, это облегчает интерпретацию кода компилятором и читателями - и это, вероятно, должно быть главной целью при написании кода.

SBI
источник
140
Это значительно ухудшает плотность кода, который вы можете упаковать в одну строку. Вы заканчиваете тем, что пишете свой код очень многословным способом; что снижает читабельность Лично я думаю, что более короткий (но не слишком короткий) код имеет тенденцию быть более читабельным (так как там меньше материала для чтения и меньше вещей, на которые нужно отвлекаться).
Ли Райан
92
Думаю, вы упустили старые времена, когда в C ++ не было стандартного stringкласса, и, похоже, у каждой библиотеки был свой. Скажем вам, что: Мы будем продолжать писать наш код std::, и вы сможете запускать наш код grep -v std:: | vimпри его просмотре. Или вы можете научить своего редактора std::использовать ключевое слово, цвет которого должен соответствовать цвету фона. Что бы ни работало.
Майк ДеСимон,
80
Я не думаю, что это std::вообще вредно. Он несет очень важную информацию (а именно: «все, что приходит после, является частью стандартной библиотеки», и это все еще довольно короткий и компактный префикс. В большинстве случаев это вообще не проблема. Иногда у вас есть несколько строк кода где вам нужно часто обращаться к определенным символам в stdпространстве имен, а затем usingоператор в этой конкретной области решает проблему хорошо, но в общем случае это не шум, а ценная информация, а также устранение неясностей.
jalf
147
Всякий раз, когда я вижу std::, я знаю, что это произойдет std::без необходимости думать об этом. Если я вижу stringили listили mapсами по себе, я немного удивляюсь.
Матин Улхак,
68
@LieRyan Тогда удачи в написании библиотеки геометрии, не называя ничего vector, transformили distance. И это только примеры многих очень распространенных имен, используемых в стандартной библиотеке. Предложение не использовать их из-за страха или предвзятого мнения о функции пространства имен, являющейся неотъемлемой частью C ++, довольно контрпродуктивно.
Кристиан Рау
420

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

Тем не менее, вы можете свободно использовать оператор using в ваших (приватных) * .cpp файлах.


Остерегайтесь, что некоторые люди не согласны с моим высказыванием «не стесняйтесь», как это - потому что, хотя usingутверждение в файле cpp лучше, чем в заголовке (потому что это не влияет на людей, которые включают ваш заголовочный файл), они думают, что это все еще не так хорошо (потому что в зависимости от кода это может усложнить реализацию класса). Эта запись C ++ Super-FAQ гласит:

Директива using существует для устаревшего кода C ++ и для облегчения перехода к пространствам имен, но вам, вероятно, не следует использовать его на регулярной основе, по крайней мере, в новом коде C ++.

FAQ предлагает две альтернативы:

  • Декларация об использовании:

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

    std::cout << "Values:";
ChrisW
источник
1
Конечно, вы никогда не должны принимать состояние глобального cout, иначе кто-то имеет std: cout << std :: hex и впоследствии не сможет выполнить std :: restore_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.

Следствие: в заголовочных файлах не пишите на уровне пространства имен, используя директивы или объявления; вместо этого явно укажите пространство имен для всех имен. (Второе правило следует из первого, потому что заголовки никогда не могут знать, какие другие заголовки #include могут появиться после них.)

обсуждение

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

mattnewport
источник
4
Еще одно мнение программиста, но, хотя я полностью согласен с утверждением, что слово usingникогда не должно появляться в заголовке, я не настолько убежден в том, что свободная лицензия может быть размещена using namespace xyz;где-либо в вашем коде, особенно если она xyzесть std. Я использую using std::vector;форму, так как она тянет только один элемент из пространства имен в псевдоглобальную область, что приводит к гораздо меньшему риску столкновения.
dgnuff
2
@ Lightness Races на орбите вы, конечно, имеете право на свое мнение. Было бы более полезно, если бы была попытка объяснить, почему вы не согласны с советом, данным в этом ответе. Особенно было бы интересно понять, в чем смысл пространств имен, если «использовать» их плохо? Почему бы просто не назвать вещи std_cout вместо std :: cout ... создатели пространства имен C ++ / должны были иметь некоторое представление, когда они пытались их создать.
Нихолку
1
@nyholku: Нет необходимости - большинство других ответов приводят те же причины, что и я. Также, пожалуйста, не стесняйтесь отметить ":)", который я добавил к моему комментарию! И то, что я не сказал, пространства имен это плохо.
Гонки легкости на орбите
Да, я заметил это :), но IMO, большинство ответов (которые идут вразрез с этим мудрым советом) ошибочны (не то, чтобы я делал какие-либо статистические данные о том, какое большинство сейчас). Если вы согласны с тем, что пространство имен «неплохо», вы можете сказать, где, по вашему мнению, они подходят, если вы не согласны с этим ответом?
Нихолку
Я не могу не чувствовать, что using namespaceзло подобно gotoзлу. Оба имеют действительное использование, но 999 раз из 1000 они будут использованы неправильно. Так что, да, using namespaceв источнике вы не будете загрязнять пространство имен других включений, аккуратно. Но это все равно не защитит вас от «веселья» , возникающего от using namespace Foo+ using namespace Barпри вызове (неявный Foo: :) baz(xyz)и внезапного взлома кода (без соответствующих изменений) только потому, что он Bar::baz()был добавлен куда-то, что просто лучше совпадение (и, следовательно, теперь
вызывается
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)).

robson3.14
источник
9
Извините, но я категорически не согласен с этим.
Билли ОНил
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::swapFOR value_, то есть void std::swap(int, int).
  • Если у вас void swap(Child&, Child&)реализована перегрузка, компилятор выберет ее.
  • Если вы не имеете , что перегрузка компилятор будет использовать void std::swap(Child&,Child&)и попробовать все эти замены.

В C ++ 11 больше нет причин использовать этот шаблон. Реализация std::swapбыла изменена, чтобы найти потенциальную перегрузку и выбрать ее.

towi
источник
5
«Реализация std :: swap была изменена, чтобы найти потенциальную перегрузку и выбрать ее». - Какая? Вы уверены, что? Хотя это правда, что swapв первую очередь предоставление каста не так уж важно в C ++ 11, так как std::swapсам он более гибкий (использует семантику перемещения). Но std::swapавтоматически выбирая свой собственный своп, это абсолютно ново для меня (и я не очень в это верю).
Кристиан Рау
@ChristianRau Я так думаю, да. Я читал это где-то на ТАК. Мы всегда можем спросить Говарда , он должен знать. Я копать и копать сейчас ...
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::содержит эти имена. Если вы также попытаетесь использовать эти имена локально, это может привести к некоторой путанице.

Если все стандартное содержимое находится в собственном пространстве имен, вам не нужно беспокоиться о конфликтах имен с вашим кодом или другими библиотеками.

STH
источник
12
+1 не говоря уже distance. все же я предпочитаю неквалифицированные имена там, где это практически возможно, так как это повышает читабельность для меня. Кроме того, я думаю, что тот факт, что мы обычно не квалифицируем вещи в устной речи и готовы тратить время на устранение возможных двусмысленностей, означает, что имеет смысл уметь понимать то, о чем идет речь, без оговорок и применяться к источнику код, который означает, что он структурирован таким образом, что понятно, о чем идет речь, даже без квалификаций.
ура и hth. - Альф
Чтобы быть справедливым, у вас не будет большинства из них, если вы не включите <iomanip>. Тем не менее, хороший момент.
einpoklum
48

Другая причина - это сюрприз.

Если я вижу cout << blah, а std::cout << blahне думаю: что это cout? Это нормально cout? Это что-то особенное?

Мартин Беккет
источник
25
Это шутка? Я искренне не могу сказать. Если нет, то я лично предположил бы, что это нормальное «бдение», если вы не доверяете коду, так как в противном случае это был бы ВНЕШНИЙ запах кода, ИМО. ... И если вы не доверяете коду, то почему вы используете его в первую очередь? Обратите внимание, что я не говорю "ДОВЕРЯЙТЕ ВСЕМ !!" но это также кажется немного надуманным, если вы, скажем, имеете дело с какой-то известной библиотекой из GitHub или чем-то еще.
Брент Риттенхаус
28
@BrentRittenhouse cout- плохой пример, потому что все это признают. Но представьте себе futureв финансовом приложении. Это договор на покупку или продажу чего-либо на указанную дату? Нет, это не так. Если бы в коде говорилось, что std::futureвас не так легко спутать.
Джеймс Холлис
2
@BrentRittenhouse может быть немного плохой пример, по крайней мере четыре разных библиотеки имеют cout. Может быть "это стандартная библиотека? Libstdc ++? Stl? Что-то еще?" И нет, не все знают std :: cout, по крайней мере, по сути, 6 из 7 новых работников, которых мы получаем, не делают. Потому что учебные программы не используют их в образовании. Я должен прогнать printfs. Или отладки () - из Qt.
Свифт - пятничный пирог
1
В самом деле? Это в значительной степени первый пример первой главы о многих книгах по С ++, во всяком случае, это (с использованием оператора вставки) единственный C ++, который знают некоторые новые боды.
Маккензм
@mckenzm Я мог бы поместить это в книгу или конспект лекции, чтобы уменьшить беспорядок, но не в коде
Мартин Беккет
45

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

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

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

Итак, все в порядке:

  1. Директивы использования на уровне функций и объявления использования внутри реализаций функций
  2. Объявления использования на уровне исходного файла внутри исходных файлов
  3. (Иногда) директивы using уровня исходного файла
Александр Полуэктов
источник
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- using namespace Her_lib).

Алексей
источник
29

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

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

Yelonek
источник
7
Это локальное объявление об использовании , совершенно отличное от использования директивы .
ВОО
25

Приятно видеть код и знать, что он делает. Если я вижу, std::coutя знаю, что это coutпоток stdбиблиотеки. Если я увижу, coutя не знаю. Это может быть coutпоток stdбиблиотеки. Или может быть на int cout = 0;десять строк выше в той же функции. Или staticпеременная с именем coutв этом файле. Это может быть что угодно.

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

Это одна из тех вещей, которые кажутся действительно хорошей идеей, если вы учитель и вам никогда не приходилось писать и поддерживать какой-либо код для жизни. Мне нравится смотреть код, где (1) я знаю, что он делает; и (2) я уверен, что человек, который его написал, знал, что он делает.

gnasher729
источник
4
Откуда вы знаете, что std :: cout << 1 не читает статическое int с именем cout в пространстве имен std, сдвигая его на единицу и выбрасывая результат? Кроме того, как вы знаете, что "<<" делает;) ??? ... кажется, что этот ответ не является хорошей точкой данных, чтобы избежать "использования".
Нихолку
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», и все.

Люди должны перестать быть такими анальными по этому поводу. Ваш учитель был прав с самого начала. Просто используйте одно пространство имен; в этом весь смысл использования пространств имен на первом месте. Вы не должны использовать более одного одновременно. Если только это не ваше. Итак, еще раз, переопределение не произойдет.

user2645752
источник
Создание столкновений не так сложно - короткие строки нравится 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 полезен, я бы подумал о создании класса, представляющего строки вместо использования 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 (из-за количества вещей в этом пространстве имен), чем другие пространства имен.

В конечном итоге это компромисс между возможностью записи и надежностью / ремонтопригодностью. Читаемость может также учитывать, но я мог видеть аргументы для этого в любом случае. Обычно я бы сказал, что надежность и ремонтопригодность важнее, но в этом случае вы будете постоянно оплачивать стоимость записи за довольно редкое влияние надежности / ремонтопригодности. «Лучший» компромисс будет определять ваш проект и ваши приоритеты.

Kevin
источник
Второй сценарий заключает сделку для меня. Снова нет пространств имен. Не может быть таких тонких изменений в функциональности, которые остаются незамеченными под капотом.
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 ++ , Бьярн Страуструп

Рохан Сингх
источник
4
Очень интересно, что этот ответ, основанный на рекомендациях других, что Бьярн Страуструп заработал -2 ... мальчик Бьярне, должно быть, был плохим и неопытным программистом, когда он ввел эту функцию в C ++
nyholku
@nyholku: Посмотри на это .
суббота,
10

Пример, в котором using namespace stdвыдается ошибка компиляции из-за неоднозначности count, которая также является функцией в библиотеке алгоритмов.

#include <iostream>

using namespace std;

int count = 1;
int main() {
    cout << count << endl;
}
Nithin
источник
2
::count--задача решена. Обычно из пространства имен std у вас будет больше информации, чем из других мест, поэтому сохранение директивы using namespace может спасти вам ввод текста.
PSkocik
Настоящая проблема здесь в том, что в C ++ все еще есть глобалы без пространства имен. Это и тот факт, что «this» неявно присутствует в методах, вызывает столько ошибок и проблем, что я даже не могу их сосчитать, даже с правильной переменной «count». ;)
Aiken Drum
9

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

namespace stdСодержит C ++ стандартные функции и переменные. Это пространство имен полезно, когда вы часто используете стандартные функции C ++.

Как упоминается на этой странице :

Утверждение, использующее пространство имен std, обычно считается плохой практикой. Альтернативой этому выражению является указание пространства имен, которому принадлежит идентификатор, с помощью оператора области действия (: :) каждый раз, когда мы объявляем тип.

И увидеть это мнение :

Нет проблем с использованием «use namespace std» в вашем исходном файле, если вы интенсивно используете пространство имен и точно знаете, что ничего не будет конфликтовать.

Некоторые люди говорили, что включать using namespace stdв исходные файлы плохую практику, потому что вы вызываете из этого пространства имен все функции и переменные. Когда вы хотите определить новую функцию с тем же именем, что и другая функция, содержащаяся в ней, namespace stdвы перегружаете функцию, и это может вызвать проблемы из-за компиляции или выполнения. Он не будет компилироваться или выполняться так, как вы ожидаете.

Как упоминается на этой странице :

Хотя этот оператор избавляет нас от ввода std :: всякий раз, когда мы хотим получить доступ к классу или типу, определенному в пространстве имен std, он импортирует все пространство имен std в текущее пространство имен программы. Давайте возьмем несколько примеров, чтобы понять, почему это не так хорошо

...

Теперь, на более позднем этапе разработки, мы хотим использовать другую версию cout, которая реализована в некоторой библиотеке под названием «foo» (например).

...

Обратите внимание на неопределенность, на какую библиотеку указывает cout? Компилятор может обнаружить это и не скомпилировать программу. В худшем случае программа все еще может скомпилироваться, но вызвать неправильную функцию, поскольку мы никогда не указывали, к какому пространству имен принадлежит идентификатор.

CryogenicNeo
источник
7

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

Доктор Ватсон
источник
7

"Почему 'использование пространства имен std;' считается плохой практикой в ​​C ++? "

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

Рассмотрим, например, написание части числового программного обеспечения. Почему я бы даже подумал о том, чтобы загрязнить свое глобальное пространство имен, урезав общий "std :: vector" до "vector", когда "vector" является одним из наиболее важных понятий проблемной области?

Solkar
источник
19
Это не просто 5 дополнительных символов; его 5 дополнительных символов каждый раз, когда вы ссылаетесь на любой тип объекта в стандартной библиотеке. Что, если вы используете стандартную библиотеку очень часто, будет часто. Так что в программе приличного размера более реалистично тысячи дополнительных символов. Предположительно, в язык была добавлена ​​директива using, чтобы ее можно было использовать ...
Джереми Фризнер
5
Это не 5 дополнительных символов каждый раз, это 5 символов и, возможно, пара щелчков мыши, чтобы открыть меню и выполнить поиск и замену в редакторе по вашему выбору.
Дейв Уолли,
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 появилось ключевое слово auto, которое еще проще, например, при использовании итераторов.
Джуззлин
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 [хотя я допускаю, что шаблоны с переменным числом аргументов очень и очень полезны!]). Даже те, которые я принимаю, были трудными, и у меня все еще есть проблемы с ними.

Питер Мортенсен
источник
1
Расширение stdпространства имен является неопределенным поведением и, следовательно, никогда не должно выполняться.
Тамбре
7

Из моего опыта, если у вас есть несколько библиотек, которые используют, скажем cout, но для другой цели вы можете использовать не так cout.

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

Engine Dev
источник
6

С неквалифицированными импортированными идентификаторами вам нужны внешние инструменты поиска, такие как grep, чтобы узнать, где идентификаторы объявлены. Это усложняет рассуждения о правильности программы.

Август Карлстрем
источник
6

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

MathGladiator
источник
6

Это плохая практика, часто называемая глобальным загрязнением пространства имен. Проблемы могут возникать, когда более чем одно пространство имен имеет одно и то же имя функции с сигнатурой, тогда компилятору будет неоднозначно решать, какой из них вызывать, и всего этого можно избежать, когда вы задаете пространство имен с помощью вызова функции подобно std::cout. Надеюсь это поможет. :)

adn.911
источник
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 выходит ...
Тоби Спейт