Что будет если убрать using namespace std
Почему «использование пространства имен std» считается плохой практикой
Хотя этот оператор избавляет нас от ввода std :: всякий раз, когда мы хотим получить доступ к классу или типу, определенному в пространстве имен std, он импортирует все пространство имен std в текущее пространство имен программы. Давайте возьмем несколько примеров, чтобы понять, почему это не так хорошо
Допустим, мы хотим использовать cout из пространства имен std. Итак, мы пишем
using namespace std;
cout » Something to Display» ;
Теперь, на более поздней стадии разработки, мы хотим использовать другую версию cout, которая реализована в некоторой библиотеке под названием «foo» (например).
using namespace std;
cout » Something to display» ;
Заметьте, как возникает неопределенность, на какую библиотеку указывает cout? Компилятор может обнаружить это и не скомпилировать программу. В худшем случае программа все еще может скомпилироваться, но вызвать неправильную функцию, поскольку мы никогда не указывали, к какому пространству имен принадлежит идентификатор.
Пространства имен были введены в C ++ для разрешения конфликтов имен идентификаторов. Это гарантировало, что два объекта могут иметь одно и то же имя и при этом обрабатываться по-разному, если они принадлежат разным пространствам имен. Обратите внимание, как в этом примере произошла полная противоположность. Вместо того, чтобы разрешать конфликт имен, мы фактически создаем конфликт имен.
Когда мы импортируем пространство имен, мы по сути вытягиваем все определения типов в текущую область. Пространство имен std огромно. Он имеет сотни предопределенных идентификаторов, поэтому разработчик может упустить из виду тот факт, что в библиотеке std есть другое определение их предполагаемого объекта. Не зная об этом, они могут продолжить указывать свою собственную реализацию и ожидать, что она будет использоваться в более поздних частях программы. Таким образом, в текущем пространстве имен будет два определения одного и того же типа. Это не разрешено в C ++, и даже если программа компилируется, нет способа узнать, какое определение где используется.
Решение проблемы состоит в том, чтобы явно указать, к какому пространству имен принадлежит наш идентификатор, с помощью оператора области действия (: :). Таким образом, одним из возможных решений приведенного выше примера может быть
// Используем cout библиотеки std
std::cout «Something to display» ;
// Используем библиотеку cout of foo
foo::cout «Something to display» ;
Но необходимость вводить std :: каждый раз, когда мы определяем тип, утомительна. Это также делает наш код более привлекательным с большим количеством определений типов и затрудняет чтение кода. Рассмотрим, например, код для получения текущего времени в программе
auto start = std::chrono::high_performance_clock::now()
Исходный код, который полон сложных и длинных определений типов, не очень легко прочитать. Это то, что разработчики стремятся избегать, так как удобство сопровождения кода для них главным образом важно.
Есть несколько способов решить эту дилемму, то есть указать точное пространство имен, не засоряя код ключевыми словами std.
Рассмотрите возможность использования typedefs
typedefs избавляют нас от написания длинных определений типов. В нашем примере 1 мы могли бы решить проблему, используя две typedefs одну для библиотеки std и другую для foo
typedef std::cout cout_std;
typedef foo::cout cout_foo;
cout_std «Something to write» ;
cout_foo «Something to write» ;
Вместо импорта целых пространств имен импортируйте усеченное пространство имен
В примере 2 мы могли бы импортировать только пространство имен хроно в std.
// Импортируем только хронологическое пространство имен под std
using namespace std::chrono;
auto start = high_performance_clock::now();
auto stop = high_performance_clock::now();
Мы также можем использовать оператор для импорта одного идентификатора. Для импорта только std :: cout мы могли бы использовать
Если вы все еще импортируете целые пространства имен, попробуйте сделать это внутри функций или в ограниченной области, а не в глобальной области.
Используйте оператор «using namespace std» внутри определений функций или классов, структурных определений. При этом определения пространства имен импортируются в локальную область, и мы, по крайней мере, знаем, где могут возникнуть возможные ошибки, если они возникнут.
using namespace std;
// Используем оператор импорта внутри ограниченной области
using namespace std;
// Продолжаем работу с функцией
Вывод.
Мы обсудили альтернативные методы доступа к идентификатору из пространства имен. Во всех случаях избегайте импорта целых пространств имен в исходный код.
В то время как для изучения и развития хороших методов кодирования может потребоваться некоторое время, они, как правило, окупаются в долгосрочной перспективе. Написание чистого, однозначного и надежного безошибочного кода должно быть целью любого разработчика программного обеспечения.
Урок №54. using-стейтменты
Обновл. 13 Сен 2021 |
Если вы часто используете Стандартную библиотеку C++, то постоянное добавление std:: к используемым объектам может быть несколько утомительным, не правда ли? Язык C++ предоставляет альтернативы в виде using-стейтментов.
Использование «using-объявления»
Одной из альтернатив является использование «using-объявления». Вот программа «Hello, world!» с «using-объявлением» в строке №5:
Использование «using-директивы»
Второй альтернативой является использование «using-директивы». Вот программа «Hello, world!» с «using-директивой» в строке №5:
Много разработчиков спорят насчет использования «using-директивы». Так как с её помощью мы подключаем ВСЕ имена из пространства имен std, то вероятность возникновения конфликтов имен значительно возрастает (но все же эта вероятность в глобальном масштабе остается незначительной). using namespace std; сообщает компилятору, что мы хотим использовать всё, что находится в пространстве имен std, так что, если компилятор найдет имя, которое не сможет распознать, он будет проверять его наличие в пространстве имен std.
Совет: Старайтесь избегать использования «using-директивы» (насколько это возможно).
Пример конфликта c «using-директивой»
Рассмотрим пример, где использование «using-директивы» создает неопределенность:
Здесь компилятор не сможет понять, использовать ли ему std::cout или функцию cout(), которую мы определили сами. В результате, получим ошибку неоднозначности. Хоть это и банальный пример, но если бы мы добавили префикс std:: к cout:
Или использовали бы «using-объявление» вместо «using-директивы»:
Тогда наша программа была бы без ошибок.
Большинство программистов избегают использования «using-директивы» именно по этой причине. Другие считают это приемлемым до тех пор, пока «using-директива» используется только в пределах отдельных функций (что значительно сокращает масштабы возникновения конфликтов имен).
Области видимости «using-объявления» и «using-директивы»
Если «using-объявление» или «using-директива» используются в блоке, то они применяются только внутри этого блока (по обычным правилам локальной области видимости). Это хорошо, поскольку уменьшает масштабы возникновения конфликтов имен до отдельных блоков. Однако многие начинающие программисты пишут «using-директиву» в глобальной области видимости (вне функции main() или вообще вне любых функций). Этим они вытаскивают все имена из пространства имен std напрямую в глобальную область видимости, значительно увеличивая вероятность возникновения конфликтов имен. А это уже не хорошо.
Правило: Никогда не используйте using-стейтменты вне тела функций.
Отмена/замена using-стейтментов
Как только один using-стейтмент был объявлен, его невозможно отменить или заменить другим using-стейтментом в пределах области видимости, в которой он был объявлен. Например:
Why “using namespace std” is considered bad practice
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
Почему «using namespace std;» считается плохой практикой?
Почему using namespace std; считается плохой практикой? Является ли это неэффективным или существует риск объявления неоднозначных переменных (переменных, которые имеют то же имя, что и функция в пространстве имен std )? Это влияет на производительность?
30 ответов
Это вообще не связано с производительностью. Но учтите: вы используете две библиотеки под названием Foo и Bar:
Я согласен со всем, что Грег написал, но хочу добавить: Может даже хуже, чем сказал Грег!
Если вы считаете это маловероятным: здесь, в Stack Overflow, был вопрос, заданный, где в значительной степени именно это и произошло (неправильная функция вызвана из-за на опущенный префикс std:: ) примерно через полгода после того, как я дал этот ответ. Вот еще один, более свежий пример такого вопроса. Так что это настоящая проблема.
За десять лет этот проект вырос до нескольких миллионов строк кода. Поскольку эти обсуждения возникают снова и снова, мне когда-то было любопытно, как часто (разрешенная) область видимости функции using фактически использовалась в проекте. Я поискал его в источниках и нашел только один или два десятка мест, где он использовался. На мой взгляд, это означает, что после попытки разработчики не находят std:: достаточно болезненным, чтобы использовать директивы using даже один раз на каждые 100 kLoC, даже если это разрешено.
Проблема с помещением using namespace в файлы заголовков ваших классов заключается в том, что это заставляет любого, кто хочет использовать ваши классы (включая ваши файлы заголовков), также «использовать» (т.е. видеть все в) эти другие пространства имен.
Директива using существует для устаревшего кода C ++ и для облегчения перехода к пространствам имен, но вам, вероятно, не следует использовать ее на регулярной основе, по крайней мере, в вашем новом коде C ++.
FAQ предлагает две альтернативы:
Просто наберите std ::
Недавно я столкнулся с жалобой на Visual Studio 2010. Оказалось, что почти все исходные файлы содержат эти две строки:
Многие функции Boost входят в C ++ 0x стандарт, а Visual Studio 2010 имеет множество функций C ++ 0x, поэтому внезапно эти программы перестали компилироваться.
Краткая версия: не используйте глобальные объявления или директивы using в файлах заголовков. Не стесняйтесь использовать их в файлах реализации. Вот что Herb Sutter и Андрей Александреску должен сказать об этой проблеме в Стандарты кодирования C ++ (выделено мной жирным шрифтом):
Резюме
Использование пространства имен предназначено для вашего удобства, а не для того, чтобы вы навязывали его другим: никогда не пишите объявление using или директиву using перед директивой #include.
Следствие: в файлах заголовков не записывайте уровень пространства имен с помощью директив или с помощью объявлений; вместо этого явно уточняйте все имена по пространству имен. (Второе правило следует из первого, потому что заголовки никогда не могут знать, какой другой заголовок #includes может появиться после них.)
Обсуждение
Не следует использовать директиву using в глобальной области видимости, особенно в заголовках. Однако бывают ситуации, когда это уместно даже в файле заголовка:
Не используйте его глобально
Вы можете использовать его локально
Идиома для локального использования
Это творит следующую магию:
В C ++ 11 больше нет причин использовать этот шаблон. Реализация std::swap была изменена, чтобы найти потенциальную перегрузку и выбрать ее.
Если все стандартные вещи находятся в собственном пространстве имен, вам не нужно беспокоиться о конфликтах имен с вашим кодом или другими библиотеками.
Опытные программисты используют все, что решает их проблемы, и избегают того, что создает новые проблемы, и они избегают директив using на уровне заголовочного файла именно по этой причине.
Итак, все в порядке:
В этом примере мы разрешили потенциальные конфликты имен и неоднозначности, связанные с их составом.
Имена, явно объявленные там (включая имена, объявленные с помощью объявлений-использования, таких как His_lib::String ), имеют приоритет над именами, доступными в другой области с помощью директивы-использования ( using namespace Her_lib ).
Это одна из тех вещей, которые кажутся действительно хорошей идеей, если вы учитель, и вам никогда не приходилось писать и поддерживать какой-либо код, чтобы заработать себе на жизнь. Мне нравится видеть код, в котором (1) я знаю, что он делает; и (2) я уверен, что человек, написавший это, знал, что он делает.
Все дело в управлении сложностью. Использование пространства имен втянет в себя вещи, которые вам не нужны, и, возможно, затруднит отладку (я говорю, возможно). Использование std :: повсюду труднее для чтения (больше текста и все такое).
Теперь предположим, что вы используете foo и bar вместе в своей собственной программе следующим образом:
На этом этапе вы получите ошибку компилятора:
Но представьте себе альтернативный сценарий, в котором bar вместо этого изменился и стал выглядеть так:
Когда вы используете пространство имен, вы рискуете подобным сценарием, поэтому людям неудобно использовать пространства имен. Чем больше элементов в пространстве имен, тем выше риск конфликта, поэтому людям может быть еще более неудобно использовать пространство имен std (из-за количества элементов в этом пространстве имен), чем другие пространства имен.
В конечном итоге это компромисс между возможностью записи и надежностью / ремонтопригодностью. Читаемость также может иметь значение, но я видел аргументы в пользу этого в любом случае. Обычно я бы сказал, что надежность и ремонтопригодность более важны, но в этом случае вы будете постоянно платить за возможность записи за довольно редкое влияние на надежность / ремонтопригодность. «Лучший» компромисс определит ваш проект и ваши приоритеты.
Так что просто считайте их функции зарезервированными именами, такими как «int» или «class», и все.
Люди должны перестать быть такими анальными по этому поводу. Ваш учитель был прав с самого начала. Просто используйте ОДНО пространство имен; в этом весь смысл использования пространств имен в первую очередь. Вы не должны использовать более одного одновременно. Если только это не твое собственное. Итак, повторного определения не произойдет.
Вы должны уметь читать код, написанный людьми, у которых другой стиль и мнение о передовых методах, чем у вас.
Повторение имени пространства имен может отвлекать как читателей, так и писателей. Следовательно, можно утверждать, что имена из определенного пространства имен доступны без явной квалификации. Например:
Пространства имен предоставляют мощный инструмент для управления различными библиотеками и разными версиями кода. В частности, они предлагают программисту альтернативу тому, как явно указать ссылку на нелокальное имя.
Источник: Обзор языка программирования C ++ Бьярне Страуструп
Пример, в котором using namespace std выдает ошибку компиляции из-за неоднозначности счетчика, который также является функцией в библиотеке алгоритмов.
Это не ухудшает производительность вашего программного обеспечения или проекта. Включение пространства имен в начало исходного кода неплохо. Включение инструкции using namespace std зависит от ваших потребностей и способа разработки программного обеспечения или проекта.
namespace std содержит стандартные функции и переменные C ++. Это пространство имен полезно, когда вы часто будете использовать стандартные функции C ++.
Оператор, использующий пространство имен std, обычно считается плохой практикой. Альтернативой этому оператору является указание пространства имен, которому принадлежит идентификатор, с помощью оператора области видимости (: 🙂 каждый раз, когда мы объявляем тип.
Нет проблем с использованием «using namespace std» в исходном файле, когда вы интенсивно используете пространство имен и точно знаете, что ничего не произойдет.
Хотя этот оператор избавляет нас от ввода std :: всякий раз, когда мы хотим получить доступ к классу или типу, определенным в пространстве имен std, он полностью импортирует пространство имен std в текущее пространство имен программы. Давайте рассмотрим несколько примеров, чтобы понять, почему это может быть не так хорошо.
Теперь, на более позднем этапе разработки, мы хотим использовать другую версию cout, которая специально реализована в некоторой библиотеке под названием «foo» (например)
Обратите внимание на неоднозначность, на какую библиотеку указывает cout? Компилятор может обнаружить это и не скомпилировать программу. В худшем случае программа все еще может компилироваться, но вызывать неправильную функцию, поскольку мы никогда не указывали, какому пространству имен принадлежит идентификатор.
Это от случая к случаю. Мы хотим минимизировать «общую стоимость владения» программного обеспечения на протяжении его срока службы. Утверждение «using namespace std» требует определенных затрат, но отсутствие его использования также требует затрат на удобочитаемость.
Люди правильно отмечают, что при его использовании, когда стандартная библиотека вводит новые символы и определения, ваш код перестает компилироваться, и вы можете быть вынуждены переименовать переменные. И все же это, вероятно, хороший долгий срок, так как будущие сопровождающие на мгновение будут сбиты с толку или отвлечены, если вы используете ключевое слово для какой-то неожиданной цели.
Вы не хотите иметь шаблон с именем, скажем, вектор, который не известен всем остальным. И количество новых определений, введенных таким образом в библиотеку C ++, достаточно мало, поэтому может просто не появиться. За внесение такого рода изменений приходится платить, но цена невысока и компенсируется ясностью, достигнутой за счет отказа от использования имен символов std для других целей.
Учитывая количество классов, переменных и функций, указание std:: для каждого из них может испортить ваш код на 50% и усложнить понимание. Алгоритм или шаг в методе, который можно было бы использовать на одном экране, полный кода, теперь требует прокрутки назад и вперед, чтобы следовать. Это реальная стоимость. Возможно, это может быть невысокая цена, но люди, которые отрицают, что это вообще существует, неопытны, догматичны или просто ошибаются.
Предлагаю следующие правила:
Я не думаю, что это обязательно плохая практика в любых условиях, но вы должны быть осторожны при ее использовании. Если вы пишете библиотеку, вам, вероятно, следует использовать операторы разрешения области видимости с пространством имен, чтобы ваша библиотека не сталкивалась с другими библиотеками. Что касается кода уровня приложения, я не вижу в этом ничего плохого.
Это зависит от того, где он находится. Если это общий заголовок, то вы уменьшаете значение пространства имен, объединяя его с глобальным пространством имен. Имейте в виду, что это может быть изящным способом создания глобальных модулей.
«Почему ‘using namespace std;’ считается плохой практикой в C ++? «
Я говорю наоборот: почему некоторые считают набор пяти дополнительных символов громоздким?
Рассмотрим, например, написание части числового программного обеспечения. Зачем мне вообще рассматривать загрязнение своего глобального пространства имен путем сокращения общего «std :: vector» до «вектора», когда «вектор» является одним из наиболее важных понятий проблемной области?
Обратите внимание, что это простой пример. Если у вас есть файлы с 20 включениями и другим импортом, вам придется пройти через множество зависимостей, чтобы выяснить проблему. Хуже всего то, что вы можете получить несвязанные ошибки в других модулях в зависимости от конфликтующих определений.
Это не ужасно, но вы избавите себя от головной боли, если не будете использовать его в файлах заголовков или в глобальном пространстве имен. Возможно, это нормально делать это в очень ограниченных пределах, но у меня никогда не было проблем с вводом дополнительных пяти символов, чтобы прояснить, откуда берутся мои функции.
Вы могли бы написать программу для этого, но не лучше ли потратить время на работу над самим проектом, а не на написание программы для поддержки вашего проекта?
Итак, вот как я это решил. Установите регулярное выражение Boost и свяжите его. Затем я делаю следующее, чтобы, когда libstdc ++ полностью реализовал его, мне нужно было только удалить этот блок, а код остался прежним:
Я не буду спорить, плохая это идея или нет. Однако я утверждаю, что он сохраняет его чистоту для моего проекта и в то же время делает его конкретным: правда, мне нужно использовать Boost, но я использую его как в конечном итоге он будет у libstdc ++. Да, запуск собственного проекта со стандартным (. ) в самом начале имеет очень большое значение для поддержки, разработки и всего, что связано с проектом!
Просто чтобы кое-что прояснить: я вообще-то не думаю, что использовать имя класса / чего угодно в STL намеренно и более конкретно вместо. Строка является для меня исключением (игнорируйте первое, приведенное выше или второе здесь, каламбур, если необходимо), поскольку мне не понравилась идея «Строка».
Использование пространства имен std
Кажется, существуют разные взгляды на использование слова using в отношении пространства имен std.
Некоторые говорят использовать ‘ using namespace std ‘, другие говорят, что нет, а вместо этого используют префикс std-функций, которые должны использоваться с ‘ std:: ‘, в то время как другие говорят, что используйте что-то вроде этого:
для всех std-функций, которые будут использоваться.
Каковы плюсы и минусы каждого из них?
Ошибка обычно длинная и недружелюбная, потому что std::count это шаблон с некоторыми длинными вложенными типами.
Это нормально, потому что std::count входит в глобальное пространство имен, а счетчик функций скрывает его.
Исключение основ (необходимость добавления std :: infront всех объектов / функций stl и меньшая вероятность конфликта, если у вас нет ‘using namespace std’)
Также стоит отметить, что никогда не следует ставить
В файле заголовка, поскольку он может распространяться на все файлы, которые включают этот файл заголовка, даже если они не хотят использовать это пространство имен.
В некоторых случаях очень полезно использовать такие вещи, как
Сначала немного терминологии:
Точно так же, если вы можете обойтись несколькими объявлениями using (вместо директив using ) для определенных типов в std пространстве имен, тогда нет причин, по которым вы не должны иметь только эти конкретные имена, перенесенные в текущее пространство имен. К тому же, я думаю, что было бы безумием и хлопотно иметь дело с 25 или 30 объявлениями using, когда одна директива using также подойдет.
Также хорошо иметь в виду, что бывают случаи, когда вы должны использовать объявление using. Обратитесь к статье 25 Скотта Мейерса: подумайте о поддержке безбрасывающего свопа из Эффективного C ++, Третье издание. Чтобы универсальная шаблонная функция использовала «лучший» метод подкачки для параметризованного типа, вам необходимо использовать объявление с использованием объявления и поиск, зависящий от аргументов (также известный как ADL или поиск по Кенигу):
Херб Саттер и Андрей Александреску говорят об этом в «Правиле 59: Не записывайте использование пространств имен в заголовочный файл или перед #include» своей книги «Стандарты кодирования C ++: 101 правила, рекомендации и лучшие практики»:
Вкратце: вы можете и должны использовать пространство имен, используя объявления и директивы в ваших файлах реализации после #include директив, и вам это нравится. Несмотря на неоднократные утверждения об обратном, декларации и директивы, использующие пространства имен, не являются злом и не противоречат цели пространств имен. Скорее, они делают пространства имен пригодными для использования.
Струпструп часто цитируется как сказал: «Не загрязняйте глобальное пространство имен» в «Языке программирования C ++, третье издание». Он действительно так говорит (C.14 [15]), но ссылается на главу C.10.1, где говорит:
Объявление using добавляет имя в локальную область видимости. С помощью директивы не делает; он просто делает имена доступными в той области, в которой они были объявлены. Например:
Локально объявленное имя (объявленное обычным объявлением или использованием-объявлением) скрывает нелокальные объявления с тем же именем, а любые незаконные перегрузки имени обнаруживаются в точке объявления.
Когда библиотеки, объявляющие много имен, становятся доступными с помощью директив using, существенным преимуществом является то, что конфликты неиспользуемых имен не считаются ошибками.
Я надеюсь увидеть радикальное сокращение использования глобальных имен в новых программах, использующих пространства имен, по сравнению с традиционными программами на C и C ++. Правила для пространств имен были специально созданы, чтобы не давать преимуществ «ленивому» пользователю глобальных имен перед тем, кто заботится о том, чтобы не загрязнять глобальную область видимости.
И как получить такое же преимущество, как «ленивый пользователь глобальных имен»? Используя преимущество директивы using, которая безопасно делает имена в пространстве имен доступными для текущей области.