Область применения декларации в пространстве имен

Безопасно (и правильно) в заголовочном файле С++ использовать декларацию использования в пространстве имен следующим образом:

#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.