Область применения декларации в пространстве имен
Безопасно (и правильно) в заголовочном файле С++ использовать декларацию использования в пространстве имен следующим образом:
#include <boost/numeric/ublas/vector.hpp>
namespace MyNamespace {
using boost::numeric::ublas::vector;
vector MyFunc(vector in);
}
т.е. это "использование boost:: numeric:: ublas:: vector", содержащееся в блоке MyNamespace, или это загрязняет пространство имен любого файла, который включает этот заголовок?
Ответы
Ответ 1
Нет, это небезопасно - оно не будет загрязнять другое пространство имен, но оно опасно по другим причинам:
Директива A using
импортирует что-либо , которое в настоящее время отображается, по имени, которое вы указали, в пространство имен, в котором вы его используете. Пока ваш using
будет доступен только пользователям MyNamespace
, другие вещи из "снаружи" будут видны вашему объявлению using
.
Итак, как это опасно при использовании в заголовке? Поскольку он будет импортировать вещи, которые видны в точке объявления, точное поведение будет зависеть от порядка заголовков, которые вы указываете перед объявлением (могут быть разные вещи, видимые из boost::numeric::ublas::vector
). Поскольку вы не можете контролировать, какие заголовки включены перед заголовком (и не должны быть!), Заголовки должны быть самодостаточными!), Это может привести к очень странным проблемам, когда ваша функция найдет одно в одном компиляционном блоке, а другое в следующий.
Как правило, объявления using
должны использоваться только после, все они включены в .cpp файл. Там также есть статья по этой точной проблеме в книге "Стандарты кодирования C++" Саттер и Александреску (пункт 59). Вот цитата: "Но вот общая ловушка: многие думают, что использование объявлений, выпущенных на уровне пространства имен (...), безопасно. Это не так. Они как минимум опасны и более тонкие и более коварные".
Даже если маловероятно, что имя using
не существует нигде (как это, вероятно, здесь), все может стать уродливым: в заголовке все объявления должны быть полностью квалифицированный. Это боль, но в противном случае могут произойти странные вещи.
Изменить: См. Миграция в пространства имен для примеров и проблема, описанная подробно.
Ответ 2
Использование с помощью объявления, как сказано в названии, является декларацией. Все объявления привязаны к охватывающему блоку (7.2), в этом случае пространство имен MyNamespace
. Он не будет виден вне этого пространства имен.
Ответ 3
Это безопасно, но оно будет загрязнять пространство имен MyNamespace. Таким образом, любой файл, содержащий этот заголовок, будет иметь функции/классы в MyNamespace.
Ответ 4
Подводя итог, нет, использование-деклараций в заголовке не нормально, даже в пространстве имен, по двум причинам. Кроме того, использование-деклараций в пространстве имен в не-заголовке подвержено ошибкам или бессмысленно (см. Конец). Использование-деклараций в заголовке не нормально, потому что:
- Они вводят имя в пространство имен, которое затрагивает все файлы, содержащие заголовок.
- Они вводят только объявления для имени, которое уже было замечено, что означает, что поведение зависит от порядка включений!
В вашем примере это означает, что:
- В
MyNamespace
, vector
теперь может разрешаться boost::numeric::ublas::vector
для любых файлов, которые включают этот заголовок: он "загрязняет" пространство имен MyNamespace
.
- Какие объявления
boost::numeric::ublas::vector
импортируются, зависит от того, какие объявления появляются перед этим использованием-объявления, которое зависит от порядка включений в файл, который включает этот заголовок, и включает все его (правильно, порядок деклараций в блоке перевода после предварительной обработки).
Per ваш комментарий от 30 мая 11 в 11:51 вы действительно хотите поведения 1, но это не работает из-за проблемы 2. Вы можете получить желаемое поведение, имея отдельный заголовок, который включен после всех остальных (и полностью квалифицирует имя в других заголовках). Однако это хрупкое и, таким образом, обескураженное, предпочтительно зарезервированное только при переходе к пространствам имен:
//--- file myheader.hpp ---
#include <boost/numeric/ublas/vector.hpp>
namespace MyNamespace {
::boost::numeric::ublas::vector MyFunc(::boost::numeric::ublas::vector in);
}
//--- file myproject_last.hpp ---
namespace MyNamespace {
using ::boost::numeric::ublas::vector;
}
//--- file myproject.cpp ---
#include "myheader.hpp"
// ...other includes
#include "myproject_last.hpp"
Подробнее см. GotW # 53: Миграция в пространства имен, это обходное решение и советы: "Пространство имен с использованием объявлений никогда не должно появляться в файлах заголовков."
Можно избежать проблемы 1, добавив неназванное пространство имен вокруг объявления-объявления (чтобы эти имена не были видимыми), а затем еще один за пределами неназванного пространства имен (чтобы сделать нужное имя видимым), но это все равно страдает от проблемы 2 и убирает заголовок:
//--- file myheader.hpp ---
#include <boost/numeric/ublas/vector.hpp>
namespace MyNamespace {
namespace {
using ::boost::numeric::ublas::vector;
vector MyFunc(vector in);
}
using MyFunc; // MyNamespace::(unique)::MyFunc > MyNamespace::MyFunc
}
Из-за этих проблем вы должны использовать только use-declarations в файлах без заголовка (.cc/.cpp): это не влияет на другие файлы, поэтому проблема 1 исключается; и все заголовки включены, поэтому проблема 2 устранена. В этом случае вопрос о том, помещаете ли вы их в пространство имен, или нет, поскольку они не влияют на другие файлы; безопаснее всегда использовать полностью квалифицированные имена в самой декларации использования (абсолютная, начиная с ::
).
Проще всего положить все объявления-объявления в верхней части файла после включений, но за пределами любых пространств имен: это безопасно, недвусмысленно, легко читается и позволяет использовать имена во всем файле. Некоторые общие отклонения:
- Использование декларации внутри функции (или структуры или класса или вложенного блока): fine. Это сводит к минимуму объем и просто вопрос вкуса: использование-декларация близко к использованию (разборчивость), но теперь они разбросаны по всему файлу (разборчивость).
-
Использование-объявления с относительным именем в пространстве имен (named): с ошибкой. Это более красноречиво и добавляет некоторую ясность (связанные имена, используемые в пространстве имен, к которым они относятся), но потенциально неоднозначно (например, включает в себя относительные пути) и безопаснее избегать:
using ::foo::bar;
namespace foo { ... }
namespace foo {
// Implicitly ::foo:bar, could be ::bar, or ::other::foo::bar.
using bar;
}
-
Использование объявления с абсолютным именем в именованном пространстве имен: бессмысленно. Это вводит имя только в пространство имен, но вам все равно, поскольку вы не должны включать файл .cc/.cpp:
namespace foo {
using ::bar;
}
-
Использование декларации в неназванном пространстве имен: бессмысленно, немного опасно. Например, если у вас есть функция в неназванном пространстве имен, скажем, деталь реализации, то вы можете использовать декларацию использования для типа возвращаемого значения или типа параметра. Это вводит имя только в это пространство имен (поэтому не может быть указано из других файлов), но опять же вам все равно, поскольку вы не должны включать файл .cc/.cpp(неназванные пространства имен для предотвращения имя столкновения во время соединения, которое здесь не применимо: это просто псевдоним времени компиляции). Хуже того, это вводит двусмысленность, если это имя уже существует!
Ответ 5
Он не будет загрязнять другие пространства имен, но он, безусловно, загрязнит пространство имен MyNamespace.