"использование пространства имен" в заголовках С++

Во всех наших курсах С++ все учителя всегда помещают using namespace std; сразу после #include в свои файлы .h. Мне кажется, что это опасно с тех пор, включив этот заголовок в другую программу, я получу пространство имен, импортированное в мою программу, возможно, не осознавая, не намереваясь или не желая (включение заголовка может быть очень глубоко вложенным).

Итак, мой вопрос двойной: я прав, что using namespace не должен использоваться в файлах заголовков и/или есть какой-то способ его отменить, например:

//header.h
using namespace std {
.
.
.
}

Еще один вопрос по тем же строкам: должен ли заголовочный файл #include использовать все заголовки, соответствующие ему .cpp, только те, которые необходимы для определений заголовков, и пусть .cpp файл #include или нет, и объявить все, что нужно, как extern?
Обоснование вопроса такое же, как и выше: я не хочу сюрпризов при включении .h файлов.

Кроме того, если я прав, это распространенная ошибка? Я имею в виду в реальном программировании и в "реальных" проектах.

Спасибо.

Ответ 1

Вы должны НЕ использовать using namespace в заголовках именно по той причине, по которой вы говорите, что он может неожиданно изменить смысл кода в любых других файлах, содержащих этот заголовок. Нет способа отменить using namespace, что является еще одной причиной, которая настолько опасна. Обычно я просто использую grep или тому подобное, чтобы убедиться, что using namespace не вызывается в заголовках, а не пытается что-то более сложное. Вероятно, статические проверки кода тоже отмечают это.

Заголовок должен содержать только заголовки, которые он должен компилировать. Простой способ обеспечить это - всегда включать каждый исходный файл в свой собственный заголовок в первую очередь перед любыми другими заголовками. Тогда исходный файл не сможет скомпилироваться, если заголовок не является автономным. В некоторых случаях, например, ссылаясь на классы детализации реализации в библиотеке, вы можете использовать форвардные объявления вместо #include, потому что у вас есть полный контроль над определением такого объявленного класса.

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

Ответ 2

Пункт 59 в Саттер и Александреску "C++ Стандарты кодирования: 101 правила, руководящие указания и передовой опыт":

59. Не записывайте использование пространства имен в заголовочном файле или перед #include.

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

Corollary: In header files, don't write namespace-level using directives or using declarations; instead, explicitly namespace-qualify all names.

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

Объявление using приносит одного приятеля. Директива using вводит всех собеседников в пространство имен. Использование вашими учителями using namespace std; является директивой использования.

Более серьезно, у нас есть пространства имен, чтобы избежать конфликта имен. Заголовочный файл предназначен для предоставления интерфейса. Большинство заголовков не зависят от того, какой код может включать их, сейчас или в будущем. Добавление операторов using для внутреннего удобства в заголовке навязывает эти удобные имена всем потенциальным клиентам этого заголовка. Это может привести к конфликту имен. И это просто грубо.

Ответ 3

Вы должны быть осторожны, когда включаете заголовки внутри заголовков. В крупных проектах он может создать очень запутанную цепочку зависимостей, которая запускает большие/длительные перестройки, чем это было на самом деле необходимо. Просмотрите эту статью и ее последующие действия, чтобы узнать больше о важности хорошей физической структуры в проектах на С++.

Вы должны включать заголовки внутри заголовка, когда это абсолютно необходимо (всякий раз, когда требуется полное определение класса), и использовать объявление вперед, где вы можете (когда требуется класс, является указателем или ссылкой).

Что касается пространств имен, я стараюсь использовать явное пространство имен в моих заголовочных файлах и помещать только using namespace в мои файлы cpp.

Ответ 4

Ознакомьтесь со стандартами кодирования космического полета Годдарда (для C и С++). Это оказалось немного сложнее, чем раньше - см. Обновленные ответы на вопросы SO:

Стандарт кодирования GSFC С++ гласит:

§3.3.7. Каждый заголовочный файл должен #include файлы, которые он должен скомпилировать, вместо того, чтобы принуждать пользователей к #include необходимым файлам. #includes должен ограничиваться потребностью заголовка; другие #includes должны быть помещены в исходный файл.

Первый из вопросов с перекрестными ссылками теперь включает цитату из стандарта кодирования GSFC C и обоснование, но вещество оказывается тем же самым.

Ответ 5

Вы правы, что заголовок using namespace в заголовке опасен. Я не знаю, как его отменить. Его легко обнаружить, но просто найдите using namespace в заголовочных файлах. По этой последней причине это необычно в реальных проектах. Более опытные сотрудники скоро пожалуются, если кто-то сделает что-то подобное.

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

Ответ 6

Вы правы. И любой файл должен включать только заголовки, необходимые этому файлу. Что касается "делает что-то неправильное в реальных проектах?" - О, да!

Ответ 7

Как и все в программировании, прагматизм должен победить над догматизмом, ИМО.

Пока вы принимаете решение по всему проекту ( "Наш проект широко использует STL, и мы не хотим, чтобы все было добавлено с помощью std::." ), я не вижу проблемы с ним. Единственное, что вы рискуете, это столкновения имен, в конце концов, и с вездесущим STL это вряд ли будет проблемой.

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

Ответ 8

Что касается "Есть ли способ отменить [a using декларация]?"

Я думаю, что полезно отметить, что объявления using зависят от области видимости.

#include <vector>

{   // begin a new scope with {
    using namespace std;
    vector myVector;  // std::vector is used
}   // end the scope with }

vector myOtherVector;   // error vector undefined
std::vector mySTDVector // no error std::vector is fully qualified

Так эффективно да. Ограничивая объем объявления using, его эффект сохраняется только в пределах этой области; он "отменен", когда эта область заканчивается.

Когда объявление using объявляется в файле за пределами любой другой области, оно имеет область видимости файла и влияет на все в этом файле.

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

Ответ 9

Я считаю, что вы можете использовать "использование" в заголовках С++ безопасно, если вы пишете объявления во вложенном пространстве имен следующим образом:

namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED
{
    /*using statements*/

    namespace DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED
    {
        /*declarations*/
    }
}

using namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED::DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED;

Это должно включать только те вещи, которые были объявлены в "DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED" без использования используемых пространств имен. Я тестировал его на компиляторе mingw64.