Почему "использование пространства имен std;" считается плохой практикой? - PullRequest
2376 голосов
/ 21 сентября 2009

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

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

Ответы [ 36 ]

2007 голосов
/ 21 сентября 2009

Это никак не связано с производительностью. Но учтите это: вы используете две библиотеки с именами Foo и Bar:

using namespace foo;
using namespace bar;

Все отлично работает, вы можете без проблем позвонить Blah() из Foo и Quux() из Bar. Но однажды вы обновляетесь до новой версии Foo 2.0, которая теперь предлагает функцию под названием Quux(). Теперь у вас есть конфликт: и Foo 2.0, и Bar импортируют Quux() в ваше глобальное пространство имен. Это займет некоторое усилие, чтобы исправить, особенно если параметры функции совпадают.

Если бы вы использовали foo::Blah() и bar::Quux(), то введение foo::Quux() было бы не событием.

1284 голосов
/ 21 сентября 2009

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

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

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

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


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

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


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

371 голосов
/ 21 сентября 2009

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

Однако вы можете не стесняться использовать оператор using в своих (личных) * .cpp файлах.


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

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

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

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

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

    std::cout << "Values:";
    
224 голосов
/ 28 октября 2010

Недавно я столкнулся с жалобой на Visual Studio 2010 . Оказалось, что почти все исходные файлы имеют следующие две строки:

using namespace std;
using namespace boost;

Многие функции Boost входят в стандарт C ++ 0x, а Visual Studio 2010 имеет много функций C ++ 0x, поэтому неожиданно эти программы не компилировались.

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

201 голосов
/ 03 ноября 2014

Краткая версия: не используйте глобальные объявления или директивы в заголовочных файлах. Не стесняйтесь использовать их в файлах реализации. Вот что Херб Саттер и Андрей Александреску должны сказать об этой проблеме в C ++ Coding Standards (выделение для акцента мое):

Основная информация

Использование пространства имен для вашего удобства, а не для того, чтобы вы навязывали его другим: никогда не пишите объявление использования или директиву использования перед директивой #include.

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

Обсуждение

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

117 голосов
/ 21 сентября 2009

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

95 голосов
/ 18 января 2013

Не используйте его глобально

Считается «плохим», только если используется глобально . Потому что:

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

76 голосов
/ 21 сентября 2009

Если вы импортируете нужные заголовочные файлы, у вас внезапно появятся имена, такие как hex, left, plus или count в вашем глобальном масштабе. Это может быть удивительно, если вы не знаете, что std:: содержит эти имена. Если вы попытаетесь использовать эти имена локально, это может привести к некоторой путанице.

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

44 голосов
/ 29 марта 2011

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

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

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

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

  1. Директивы использования уровня функций и объявления использования внутри реализаций функций
  2. Декларации использования на уровне исходного файла внутри исходных файлов
  3. (Иногда) директивы using на уровне исходного файла
41 голосов
/ 21 сентября 2009

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

Если я вижу cout << blah вместо std::cout << blah

Я думаю, что это cout? Это нормально cout? Это что-то особенное?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...