Пространства имён появились в 1993 году. Наверное, с тех пор и идут споры о директиве using namespace std и других подобных. Посмотрим, хорошо это или плохо и как пишут в разных компаниях (Google, Epic Games, Qt Company).
Что мы обычно видим.
#include <iostream>
using namespace std;
int main()
{
cout << "Hello, world!" << endl;
return 0;
}
На первый взгляд, удобно. Но в последующем возможны конфликты имён.
#include <iostream>
int main()
{
using namespace std;
cout << "Hello, world!" << endl;
return 0;
}
Не намного лучше. Конфликты имён всё ещё возможны, хотя искать ошибки будет уже проще.
Что имеется в виду под конфликтами имён? Ваша функция может называться count, а в заголовочном файле <algorithms> объявляется шаблон функции с таким же именем. Директива using namespace std; может привести к неоднозначности.
Есть и более страшные ситуации. Предположим, вы пользуетесь двумя библиотеками с пространствами имён Foo и Bar. В этих библиотеках есть функции Foo::foo() и Bar::bar(). Вы вываливаете все имена в одну кучу.
using namespace Foo;
using namespace Bar;
А в новой версии второй библиотеки появляется Bar::foo(), которая с точки зрения компилятора является лучшим вариантом (без неоднозначности), чем Foo::foo(). Код компилируется без ошибок, но «молча» вызывается совсем не та функция, которая ожидается. Такая ситуация более вероятна, чем кажется. Это могут быть две математические библиотеки со схожими, но различными наборами функций.
Если не хочется использовать квалифицированные имена, то есть писать std::cout и так далее, то можно их сперва отдельно перечислить.
#include <iostream>
int main()
{
using std::cout;
using std::endl;
cout << "Hello, world!" << endl;
return 0;
}
И самый беспроблемный вариант — квалифицированные имена.
#include <iostream>
int main()
{
std::cout << "Hello, world!" << std::endl;
return 0;
}
Ещё в 1994 году в своей книге «Дизайн и эволюция C++» Бьерн Страуструп склоняется к двум последним вариантам.
«Я рассматриваю using-директивы в основном как средство, применимое только в переходный период; авторы новых программ смогут избежать... затруднений, если будут по возможности употреблять квалифицированные имена и using-объявления».
Вот простой пример. Вместо пользовательской функции swap() вызывается std::swap(). Больше того: если убрать using namespace std;, то компиляция должна завершаться с ошибкой (аргументы типа int*, а передаются int).
#include <iostream>
using namespace std;
void swap(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int a = 0;
int b = 0;
...
swap(a, b);
...
}
Пространство имён std содержит большое количество идентификаторов, многие из которых вполне общие, например, distance.
Однозначно, писать using namespace в глобальной области видимости в заголовочном файле — плохая идея (кроме пользовательских литералов). Так указано в C++ Core Guidelines, редактируемых Бьерном Страуструпом и Гербом Саттером.
// bad.h
#include <iostream>
using namespace std; // bad
// user.cpp
#include "bad.h"
bool copy(/*... some parameters ...*/); // some function that happens to be named copy
int main()
{
copy(/*...*/); // now overloads local ::copy and std::copy, could be ambiguous
}
Те же гайдлайны позволяют писать using namespace std (как одну из фундаментальных библиотек). Но если придётся добавить заголовочные файлы иной библиотеки, то такое правило тут же перестанет действовать. Особенно это касается библиотеки Boost, ведь многие вещи из неё перебираются в стандартную библиотеку.
#include <string>
#include <vector>
#include <iostream>
#include <memory>
#include <algorithm>
using namespace std;
// ...
Гайдлайн компании Google прямо запрещает использовать using-директивы.
// Forbidden -- This pollutes the namespace.
using namespace foo;
Гайдлайн разработчиков Unreal Engine:
«Не используйте using-объявления в глобальной области видимости, даже в cpp-файлах (это приведёт к проблемам с нашей системой сборки unity)».
«Можно использовать using-объявления лишь для создания псевдонимов конкретных переменных [...] (например, Foo::FBar). Однако же, обычно мы так не пишем в коде движка Unreal».
Гайдлайны Qt Creator:
«Не используйте using-директивы в заголовочных файлах.
Не полагайтесь на using-директивы при определении классов и функций, вместо этого определяйте их в соответствующим образом названных областях, где они объявлены.
Не полагайтесь на using-директивы для доступа к глобальным функциям.
В остальных случаях следует использовать using-директивы, поскольку они помогают избегать захламления кода. Желательно располагать все using-директивы в начале файла, сразу после всех #include-директив».
[in foo.cpp]
...
#include "foos.h"
...
#include <utils/filename.h>
...
using namespace Utils;
namespace Foo {
namespace Internal {
void SomeThing::bar()
{
FilePath f; // or Utils::FilePath f
...
}
...
} // namespace Internal // or only // Internal
} // namespace Foo // or only // Foo
Чтобы понять второй пункт, сравните код выше с примером, который по этому гайдлайну считается ошибочным.
using namespace Foo;
void SomeThing::bar()// Wrong if Something is in namespace Foo
{
...
}
Гайдлайн разработчиков инструмента LLVM для создания компиляторов:
«Мы предпочитаем явно указывать префиксы всех идентификаторов из стандартного пространства имён std::, а не полагаться на директиву using namespace std;.
[...] Явные префиксы пространств имён делают код чистым [...] и более переносимым, поскольку не могут возникнуть конфликты имён между кодом LLVM и другими пространствами имён. [...]
Исключение из общего правила (то есть это не исключение для пространства имён std) — файлы реализации. Например, весь код в проекте LLVM находится в пространстве имён llvm. Нормально (и даже более чисто) писать в начале cpp-файлов директиву using namespace llvm; после директив #include [...]».
Если всё-таки хочется более короткой записи, а нужные идентификаторы используются в небольшой части кода, её можно искусственно ограничить.
{
using namespace A;
// ...
}
Некоторые пространства имён и правда громоздкие.
auto start = std::chrono::steady_clock::now();
// ...
auto end = std::chrono::steady_clock::now();
Объявление using std::chrono::steady_clock::now; сократит код. Или псевдонимы, например, namespace clock = std::chrono::steady_clock; (clock::now()).
Гайдлайн компании Google говорит, что псевдонимы нельзя использовать в заголовочных файлах в глобальной области видимости.
Тонкий приём — using-объявление в шаблонной функции. Если специфическая функция для класса существует, то будет вызвана она, иначе будет вызвана общая функция.
void f3(N::X& a, N::X& b)
{
using std::swap; // make std::swap available
swap(a, b); // calls N::swap if it exists, otherwise std::swap
}