область использования объявления в пространстве имен - PullRequest
39 голосов
/ 30 мая 2011

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

#include <boost/numeric/ublas/vector.hpp>
namespace MyNamespace {
    using boost::numeric::ublas::vector;
    vector MyFunc(vector in);
}

т.е. "using boost :: numeric :: ublas :: vector" правильно содержится в блоке MyNamespace, или это будет загрязнять пространство имен любого файла, содержащего этот заголовок?

Ответы [ 5 ]

37 голосов
/ 30 мая 2011

Нет, это не безопасно - оно не будет загрязнять другое пространство имен, но это опасно по другим причинам:

Директива using импортирует все , которое в данный момент видно по указанному вами имени, в пространство имен, где вы его используете. Хотя ваш using будет виден только пользователям MyNamespace, другие элементы извне будут видны для вашей декларации using.

Так как же это опасно при использовании в заголовке? Поскольку он будет импортировать вещи, которые видны в точке объявления, точное поведение будет зависеть от порядка заголовков, которые вы включаете перед объявлением (из boost::numeric::ublas::vector могут быть видны разные вещи). Поскольку вы не можете реально контролировать, какие заголовки включены перед вашим заголовком (и при этом вы не должны быть! Заголовки должны быть самодостаточными!), Это может привести к очень странным проблемам, когда ваша функция найдет одну вещь в одном модуле компиляции, а другую - в следующий.

Как правило, объявления using должны использоваться только после всех включений в файл .cpp. В книге "Стандарты кодирования C ++" Саттера и Александреску также есть пункт, посвященный именно этому вопросу (пункт 59). Вот цитата: «Но вот обычная ловушка: многие думают, что использование деклараций, выпущенных на уровне пространства имен (...), безопасно. Это не так. Они, по крайней мере, столь же опасны, и более тонким и коварным способом».

Даже если маловероятно, что имя, которым вы являетесь using, не существует где-либо еще (как, вероятно, имеет место здесь), все может стать уродливым: в заголовке все объявления должны быть полностью квалифицированный. Это боль, но в противном случае могут случиться странные вещи.

Также см. Миграция в пространства имен , Объявления использования и псевдонимы пространства имен и Имена пространства имен для примеров и подробно описанной проблемы.

12 голосов
/ 30 мая 2011

Использование объявления, как следует из названия, является объявлением.Все объявления ограничены включающим блоком (7.2), в данном случае пространством имен MyNamespace.Он не будет виден за пределами этого пространства имен.

4 голосов
/ 30 мая 2011

Это безопасно, но оно загрязнит пространство имен MyNamespace. Таким образом, любой файл, содержащий этот заголовок, будет иметь функции / классы в MyNamespace.

1 голос
/ 22 мая 2016

Подводя итог, нет , объявления использования в заголовке не в порядке , даже в пространстве имен, по 2 причинам.Кроме того, объявления использования в пространстве имен в не заголовке подвержены ошибкам или бессмысленны (см. Конец).Объявления использования в заголовке не подходят, потому что:

  1. Они вводят имя в пространство имен, что влияет на все файлы , которые включают заголовок.
  2. Онивводите только объявления для имени, которое уже было замечено, что означает, что поведение зависит от порядка включений!

В вашем примере это означает, что:

  1. В пределах MyNamespace, vector теперь может преобразовываться в boost::numeric::ublas::vector для любых файлов, которые содержат этот заголовок: он «загрязняет» пространство имен MyNamespace.
  2. Какие boost::numeric::ublas::vector объявления являютсяИмпорт зависит от того, какие объявления появляются перед этим объявлением использования, что зависит от порядка включения в файл, который включает этот заголовок, и все его включает (правильно, порядок объявлений в модуле перевода, после предварительной обработки).

За ваш комментарий от 30 мая '11 в 11: 51 вы на самом деле хотите поведение 1, ноэто не работает, из-за проблемlem 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"

См. ПОЛУЧИЛ # 53: Перенос в пространства имен для получения подробной информации, этого обходного пути и совета:«Пространство имен, использующее объявления, никогда не должно появляться в заголовочных файлах.»

Можно избежать проблемы 1, добавив безымянное пространство имен вокруг объявления using (чтобы эти имена не были видны), а затем еще одно внебезымянное пространство имен (чтобы сделать нужное имя само по себе видимым), но оно по-прежнему страдает от проблемы 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
}

Из-за этих проблем вы должны использовать толькообъявления в файлах без заголовка (.cc / .cpp): это не влияет на другие файлы, поэтому проблема 1 исключается;и все заголовки были включены, поэтому проблема 2 устранена.В этом случае это вопрос вкуса, помещаете ли вы их в пространство имен или нет, так как они не влияют на другие файлы;наиболее безопасно всегда использовать полностью определенные имена в самом объявлении использования (абсолютное, начиная с ::).

Самое простое - поместить все объявления использования в верхнюю часть файла после включения,но вне каких-либо пространств имен: это безопасно, однозначно, легко читается и позволяет использовать имена во всем файле.Некоторые распространенные отклонения:

  1. Объявление использования внутри функции (или структуры, или класса, или вложенного блока): штраф .Это минимизирует область видимости и является лишь делом вкуса: объявление-использование близко к использованию (разборчивость выиграна), но теперь они разбросаны по всему файлу (потеря разборчивости).
  2. Объявление-использование сотносительное имя в (именованном) пространстве имен: подвержено ошибкам .Это является более кратким и добавляет некоторую ясность (связанные имена, используемые в пространстве имен, к которому они относятся), но является потенциально неоднозначным (так же, как включает в себя с относительными путями), и его безопаснее избегать:

    using ::foo::bar;
    namespace foo { ... }
    
    namespace foo {
        // Implicitly ::foo:bar, could be ::bar, or ::other::foo::bar.
        using bar;
    }
    
  3. Объявление использования с абсолютным именем в именованном пространстве имен: бессмысленно .Это вводит имя только в пространство имен, но вас это не должно волновать, поскольку вы не должны включать файл .cc / .cpp:

    namespace foo {
        using ::bar;
    }
    
  4. Объявление использования в безымянном пространстве имен: бессмысленно, слегка опасно . Например, если у вас есть функция в безымянном пространстве имен, скажем, подробности реализации, то вы можете использовать декларацию using для ее типа возвращаемого значения или типа параметров. Это вводит имя только в это пространство имен (поэтому на него нельзя ссылаться из других файлов), но, опять же, вас это не должно волновать, поскольку вам не следует включать файл .cc / .cpp (безымянные пространства имен специально предназначены для избежания Конфликты имен во время ссылки, что здесь неприменимо: это просто псевдоним во время компиляции). Хуже того, это вносит двусмысленность, если это имя уже существует!

1 голос
/ 30 мая 2011

Он не будет загрязнять любые другие пространства имен, но, безусловно, будет загрязнять пространство имен MyNamespace.

...