Why is using namespace std considered bad practice?
Here, the compiler throws an error because it doesn’t know whether you refer to your swap global variable, or the std::swap function inside the <algorithm> header.
That is to say, using namespace is considered bad practice because it leads to name collisions, and it also makes your code less clear. If you absolutely must, you can use a using-declaration inside a local scope:
Почему не стоит использовать using namespace std
The statement using namespace std is generally considered bad practice. The alternative to this statement is to specify the namespace to which the identifier belongs using the scope operator(::) each time we declare a type.
Although the statement saves us from typing std:: whenever we wish to access a class or type defined in the std namespace, it imports the entirety of the std namespace into the current namespace of the program. Let us take a few examples to understand why this might not be such a good thing
Let us say we wish to use the cout from the std namespace. So we write
Example 1:
Now at a later stage of development, we wish to use another version of cout that is custom implemented in some library called “foo” (for example)
Notice now there is an ambiguity, to which library does cout point to? The compiler may detect this and not compile the program. In the worst case, the program may still compile but call the wrong function, since we never specified to which namespace the identifier belonged.
Namespaces were introduced into C++ to resolve identifier name conflicts. This ensured that two objects can have the same name and yet be treated differently if they belonged to different namespaces. Notice how the exact opposite has occurred in this example. Instead of resolving a name conflict, we actually create a naming conflict.
When we import a namespace we are essentially pulling all type definitions into the current scope. The std namespace is huge. It has hundreds of predefined identifiers, so it is possible that a developer may overlook the fact there is another definition of their intended object in the std library. Unaware of this they may proceed to specify their own implementation and expect it to be used in later parts of the program. Thus there would exist two definitions for the same type in the current namespace. This is not allowed in C++, and even if the program compiles there is no way of knowing which definition is being used where.
The solution to the problem is to explicitly specify to which namespace our identifier belongs to using the scope operator (::). Thus one possible solution to the above example can be
2.8 – Конфликты имен и пространства имен
Допустим, вы впервые едете к другу, и вам дан адрес: Фронт-стрит, 245 в Милл-Сити. Достигнув Милл-Сити, вы открываете свою карту и обнаруживаете, что на самом деле в Милл-Сити есть две разные улицы Фронт-стрит, расположенные на разных концах города! Куда бы вы поехали? Если нет дополнительных подсказок, которые помогли бы вам принять решение (например, вы помните, что его дом находится у реки), вам придется позвонить своему другу и попросить дополнительную информацию. Поскольку это сбивает с толку и неэффективно (особенно для почтальона), в большинстве стран все названия улиц и адреса домов в городе должны быть уникальными.
Точно так же C++ требует, чтобы все идентификаторы были однозначными. Если два идентичных идентификатора вводятся в одну и ту же программу таким образом, что компилятор или компоновщик не может их различить, компилятор или компоновщик выдаст ошибку. Эта ошибка обычно называется конфликтом имен (или коллизией имен).
Пример коллизии имен
Когда компилятор компилирует эту программу, он независимо компилирует a.cpp и main.cpp , и каждый файл компилируется без проблем.
Однако, когда работает компоновщик, он слинкует все определения в a.cpp и main.cpp вместе и обнаружит конфликтующие определения для функции myFcn . После этого компоновщик прервет работу, выдав ошибку. Обратите внимание, что эта ошибка возникает, даже если myFcn никогда не вызывается!
Большинство конфликтов имен возникают в двух случаях:
- Два (или более) определения функции (или глобальной переменной) вводятся в отдельные файлы, которые компилируются в одну программу. Это приведет к ошибке компоновщика, как показано выше.
- Два (или более) определения функции (или глобальной переменной) вводятся в один и тот же файл (часто через #include ). Это приведет к ошибке компилятора.
По мере того, как программы становятся больше и используют больше идентификаторов, вероятность возникновения конфликта имен значительно возрастает. Хорошей новостью является то, что C++ предоставляет множество механизмов для предотвращения конфликтов имен. Одним из таких механизмов является локальная область видимости, которая не позволяет локальным переменным, определенным внутри функций, конфликтовать друг с другом. Но локальная область видимости не работает для имен функций. Так как же нам уберечь имена функций от конфликта друг с другом?
Что такое пространство имен?
Вернемся на мгновение к нашей аналогии с адресом: наличие двух Фронт-стрит было проблемой только потому, что эти улицы существовали в одном городе. С другой стороны, если бы вам пришлось доставлять почту по двум адресам: один на Фронт-стрит, 209 в Милл-Сити, а другой – на Фронт-стрит, 417 в Джонсвилле, путаницы в том, куда идти, не возникнет. Другими словами, города предоставляют группы, которые позволяют нам устранять неоднозначность адресов, которые в противном случае могли бы конфликтовать друг с другом. Пространства имен действуют так же, как города в этой аналогии.
Пространство имен – это область, которая позволяет вам объявлять имена внутри него с целью устранения неоднозначности. Пространство имен обеспечивает область видимости (называемую областью пространства имен) для имен, объявленных внутри него, что просто означает, что любое имя, объявленное внутри пространства имен, не будет ошибочно принято за идентичные имена в других областях видимости.
Ключевой момент
Имя, объявленное в пространстве имен, не будет ошибочно принято за идентичное имя, объявленное в другой области видимости.
В пространстве имен все имена должны быть уникальными, в противном случае возникнет конфликт имен.
Пространства имен часто используются для группировки в большом проекте связанных идентификаторов, чтобы избежать непреднамеренного конфликта с другими идентификаторами. Например, если вы поместите все свои математические функции в пространство имен, называемое math , тогда ваши математические функции не будут конфликтовать с функциями с такими же именами вне пространства имен math .
На следующем уроке мы поговорим о том, как создавать собственные пространства имен.
Глобальное пространство имен
В C++ любое имя, которое не определено внутри класса, функции или пространства имен, считается частью неявно определенного пространства имен, называемого глобальным пространством имен (иногда также называемым глобальной областью видимости).
В примере в начале урока функции main() и обе версии myFcn() определены внутри глобального пространства имен. Конфликт имен, обнаруженный в этом примере, происходит из-за того, что обе версии myFcn() попадают в глобальное пространство имен, что нарушает правило, согласно которому все имена в пространстве имен должны быть уникальными.
Пространство имен std
Когда C++ был только разработан, все идентификаторы в стандартной библиотеке C++ (включая std::cin и std::cout ) были доступны для использования без префикса std:: (они были частью глобального пространства имен). Однако это означало, что любой идентификатор в стандартной библиотеке потенциально может конфликтовать с любым именем, которое вы выбрали для своих собственных идентификаторов (также определенных в глобальном пространстве имен). Код, который работал, мог внезапно получить конфликт имен, когда вы включили через #include новый файл из стандартной библиотеки. Или, что еще хуже, программы, которые будут компилироваться под одной версией C++, могут не компилироваться под будущей версией C++, поскольку новые идентификаторы, введенные в стандартную библиотеку, могут иметь конфликт имен с уже написанным кодом. Таким образом, C++ переместил все функции стандартной библиотеки в пространство имен с именем std (сокращение от «standard», «стандарт»).
Оказывается, имя std::cout на самом деле не std::cout . На самом деле это просто cout , а std – это имя пространства имен, частью которого является идентификатор cout . Поскольку cout определен в пространстве имен std , имя cout не будет конфликтовать с любыми объектами или функциями с именем cout , которые мы создаем в глобальном пространстве имен.
Точно так же при доступе к идентификатору, который определен в пространстве имен (например, std::cout ), вам необходимо сообщить компилятору, что мы ищем идентификатор, определенный внутри пространства имен ( std ).
Ключевой момент
Когда вы используете идентификатор, который определен внутри пространства имен (например, пространство имен std ), вы должны сообщить компилятору, что этот идентификатор находится внутри этого пространства имен.
Есть несколько способов сделать это.
Явный квалификатор пространства имен std::
Самый простой способ сообщить компилятору, что мы хотим использовать cout из пространства имен std , – это явно использовать префикс std:: . Например:
Символы :: – это оператор, называемый оператором разрешения области видимости. Идентификатор слева от символов :: определяет пространство имен, в котором содержится имя справа от символов :: . Если идентификатор слева от символа :: не указан, предполагается глобальное пространство имен.
Поэтому, когда мы говорим std::cout , мы говорим « cout , который находится в пространстве имен std ».
Это самый безопасный способ использования cout , потому что нет двусмысленности в том, на какой cout мы ссылаемся (на тот, который находится в пространстве имен std ).
Лучшая практика
Используйте явные префиксы пространств имен для доступа к идентификаторам, определенным в этих пространствах имен.
using namespace std (и почему его следует избегать)
Другой способ получить доступ к идентификаторам внутри пространства имен – использовать инструкцию директивы using . Вот наша исходная программа HelloWorld с директивой using :
Директива using указывает компилятору проверять указанное пространство имен при попытке разрешить идентификатор, не имеющий префикса пространства имен. Итак, в приведенном выше примере, когда компилятор определяет, что такое идентификатор cout , он проверяет как локально (где он не определен), так и в пространстве имен std (где он будет соответствовать std::cout ).
Многие тексты, руководства и даже некоторые компиляторы рекомендуют или используют директиву using в верхней части программы. Однако при таком использовании это плохая практика, и она крайне не рекомендуется.
Рассмотрим следующую программу:
Приведенная выше программа не компилируется, потому что теперь компилятор не может определить, нужна нам функция cout , которую мы определили, или cout , которая определена внутри пространства имен std .
При использовании директивы using таким образом любой идентификатор, который мы определяем, может конфликтовать с любым идентификатором с таким же именем в пространстве имен std . Хуже того, хотя имя идентификатора может не конфликтовать сегодня, оно может конфликтовать с новыми идентификаторами, добавленными в пространство имен std в будущих версиях языка. В этом изначально и заключалась вся суть перемещения всех идентификаторов стандартной библиотеки в пространство имен std !
Предупреждение
Избегайте использования директив (таких как using namespace std; ) в верхней части вашей программы. Они нарушают причину, по которой изначально были добавлены пространства имен.
Подробнее об инструкциях using (и соответственно о том, как их использовать) мы поговорим в уроке «6.12 – Инструкции using ».
“using namespace std” is a BAD practice in C++
Yup, you got it right. The line of code we are so addicted to and is often considered as a common practice is actually a bad practice and here is why so. So considering the fact that we most often need to access many class or types declared in the standard namespace, and for that we need the namespace std , it actually imports all of the std namespace into our current namespace. And this is actually not good at all. Let’s see how.
Now the above code, is quite simple and will work fine. But if we have another header file containing some other functionality of standard class cin or cout , it will give us an error. Here we have an ambiguity, that to which cin or cout does the compiler needs to access ?! Here the compiler will may detect this and not compile the code. If we are really unlucky, it may compile the code and call the wrong function, since we never specified ed to which namespace the identifier belonged.
Namespace were introduced in C++ to resolve identifier name conflicts, so that 2 objects can have same name and yet treated differently if they have different namespaces. And as a matter of fact, what the namespaces were introduced to prevent is what they are causing, conflict ! When we import a namespace we are essentially pulling all type definitions into the current scope. Keeping in mind the size of the std namespace (its very HUGE !), we need to avoid the use of namespace std and find a solution to this. And OH ! we have a solution.
Yes the solution to the above problem is using the keyword std:: . Although namespace std helps us avoid writing this keyword again and again, we don’t need to discuss more about the harm it may cause in case of using 2 classes having functions with same name. Although you may think we have our problem solved, but its just the beginning. The scope operator (::) and std keyword solve the purpose but also makes the code look not so user-friendly and difficult to read. Considering an example for the same :
I really like how using the std and scope operator (::) has made look like a total mess and is not at all good for the health of new programmers and end users to understand. Well we all need another solution for the same, because we know if we save ambiguity, its gonna cost us clean and tidy code. Well in no time do we have our another shining solution ready. Yup, it is the use of typedef . It saves us from writing long type definitions, and makes our code look pretty. We can make different typedefs for different class or headers.
Well now that we have a nice and tidy solution, I can discuss some tips or other ways for the same solution. For example instead of importing the entire namespace, we can simply import the truncated namespaces. This is only good if you know which class or type you need to access. For example using namespace std::chrono . This can solve our problem and we can now just simply use the functions in the chrono class.
And if you are really a rebel, and are determined to use the entire namespace and import it , try to do so inside the scope of the function. Try this approach because even if we import the entire namespace, it will only be valid in the scope of the function and will avoid the ambiguity most of the times. So I guess we now know all the possible alternatives of using or not using the namespace std . And in all we can determine is that we must avoid importing the entire library.