34

В чем проблема с "using namespace std;"?

36

Я слышал, что использование using namespace std; считается плохой практикой, и что мне следует прямо использовать std::cout и std::cin.

Почему это так? Не возникает ли риска объявления переменных с такими же именами, как что-то в пространстве имен std? Есть ли проблемы с производительностью?

5 ответ(ов)

28

Когда вы работаете с двумя библиотеками, Foo и Bar, и используете конструкцию using namespace, это может привести к конфликтам имен, особенно при обновлении одной из библиотек. Рассмотрим следующий пример:

using namespace foo;
using namespace bar;

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

Чтобы избежать таких ситуаций, настоятельно рекомендуется явно указывать пространство имен при вызове функций. Например, вместо того чтобы использовать using namespace, вы можете писать:

foo::Blah();
bar::Quux();

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

1

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

Это считается "плохим" подходом только когда используется глобально. Потому что:

  • Вы загромождаете пространство имен, в котором работаете.
  • Читателям будет сложно понять, откуда взят тот или иной идентификатор, если вы используете множество 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;      // делаем `std::swap` доступным
    // меняем местами все члены
    swap(a.value_, b.value_); // `std::swap(int, int)`
    swap(a.child_, b.child_); // `swap(Child&, Child&)` или `std::swap(...)`
}

Это делает следующую магию:

  • Компилятор выберет std::swap для value_, т.е. void std::swap(int, int).
  • Если у вас есть перегрузка void swap(Child&, Child&), компилятор выберет ее.
  • Если вы не имеете этой перегрузки, компилятор использует void std::swap(Child&, Child&) и постарается как можно лучше выполнить операцию swap.

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

0

Еще одна причина – это неожиданность.

Если я вижу cout << blah, а не std::cout << blah, у меня возникает вопрос: Что это за cout? Это тот самый обычный cout? Или это что-то особенное?

0

Я согласен, что использование using namespace глобально не рекомендуется, но его вполне допустимо применять локально, например, в namespace. Вот пример из книги "Язык программирования C++":

namespace My_lib {

    using namespace His_lib; // Все из His_lib
    using namespace Her_lib; // Все из Her_lib

    using His_lib::String; // Разрешаем возможное конфликтующее имя в пользу His_lib
    using Her_lib::Vector; // Разрешаем возможное конфликтующее имя в пользу Her_lib

}

В этом примере мы разрешили потенциальные конфликты имен и неоднозначности, возникшие в результате их составления.

Имена, явно объявленные в этом пространстве имен (включая имена, объявленные с помощью using-declaration, такие как His_lib::String), имеют приоритет над именами, доступными в другом контексте через using-directive (using namespace Her_lib).

0

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

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

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

Таким образом, следующие случаи являются приемлемыми:

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