объем использования объявления в пространстве имен

Безопасно (и правильно) ли в заголовочном файле 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, или это будет загрязнять пространство имен любого файла, который включает этот заголовок?


person Brett Ryland    schedule 30.05.2011    source источник
comment
Что именно вы имеете в виду под пространством имен любого файла? Он будет загрязнять пространство имен MyNamespace в любой единице перевода, начиная с момента объявления объявления using и далее.   -  person CB Bailey    schedule 30.05.2011
comment
для одного символа ... почему бы вам не использовать typedef?   -  person Matthieu M.    schedule 30.05.2011
comment
@Matthieu: Потому что boost::numeric::ublas::vector - это шаблон. Раньше я использовал стандартный обходной путь typedef шаблона (stackoverflow.com/questions/26151/), но хотел немного упростить.   -  person Brett Ryland    schedule 30.05.2011
comment
ах! В C ++ 0x у вас есть способы использовать псевдонимы для шаблонов ... хотя вам нужно повторно объявить все аргументы, которые вы хотите использовать, но в противном случае, я думаю, вы застряли.   -  person Matthieu M.    schedule 30.05.2011


Ответы (5)


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

Директива using импортирует все, что в данный момент отображается по указанному вами имени, в пространство имен, в котором вы его используете. В то время как ваш using будет виден только пользователям MyNamespace, другие вещи "извне" будут видны в вашем using объявлении.

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

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

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

См. Также Переход в пространства имен, Использование-объявлений и псевдонимов пространств имен и именования пространств имен для примеров и подробно описанной проблемы.

person ltjax    schedule 30.05.2011
comment
Обратите внимание, что я использую using boost::numeric::ublas::vector, а не using namespace boost::numeric::ublas, поскольку я не хочу импортировать все пространство имен boost::numeric::ublas. Кроме того, поскольку это объявлено внутри блока namespace MyNamespace { }, разве вектор не должен быть неоднозначным, только если кто-то написал что-то вроде using namespace std; using namespace MyNamespace;? - person Brett Ryland; 30.05.2011
comment
Нет, это будет неоднозначно, поскольку std :: vector имеет псевдоним vector и используется в вашем заголовке (потому что заголовок включен после using namespace std). Он даже не скомпилируется! - person ltjax; 30.05.2011
comment
Использование одного имени потенциально немного менее страшно, чем импорт всего пространства имен, но, тем не менее, возникают те же проблемы. - person ltjax; 30.05.2011
comment
@ltjax: Что именно будет неоднозначным? Внутри MyNamespace vector, введенный с помощью объявления, скроет любой вектор, видимый в глобальном пространстве имен, введенный с помощью директивы using . Неужто это намерение? - person CB Bailey; 30.05.2011
comment
@ Чарльз: ой, ты прав, мне плохо. Дело все еще в силе. У Саттера есть хороший пример по этому поводу, но он использует функции. Просто нужно найти один с типами. - person ltjax; 30.05.2011
comment
Дело все еще в силе. Возможно, хотя должен сказать, что пока не понимаю, в чем проблема с типами. - person CB Bailey; 30.05.2011
comment
Например, кто-то может определить MyFunction::boost::numeric::ublas::vector, и это изменит значение кода в заголовке. Но это маловероятно и легко может быть исправлено using ::boost::numeric::ublas::vector. В примере Саттера используются перегрузки функций, поэтому я полагаю, что специализация шаблонов может испортить ситуацию с типами. - person ltjax; 30.05.2011
comment
@Charles: В опубликованной мной ссылке GotW есть пример, хотя он не кажется таким серьезным, как использование объявлений функций, потому что он не меняет значение молча. Обычно компилятор жалуется на двусмысленность. - person ltjax; 30.05.2011
comment
@ltjax: Итак, в заключение, если бы я использовал using ::boost::numeric::ublas::vector внутри блока пространства имен в моем файле заголовка, помогло бы это избежать возможных двусмысленностей в моем файле заголовка? Если кто-то позже вызовет using namespace std; using namespace MyNamespace в файле .cpp, это вызовет двусмысленность, но также _3 _..., так что особой проблемы нет, да? - person Brett Ryland; 30.05.2011
comment
@Brett: это позволило бы избежать некоторых из наиболее опасных двусмысленностей, но вы не совсем в безопасности. Например, кто-то может реализовать другой vector в MyNamespace (и это не обязательно должны быть вы), и в этом случае вы получите ошибку компиляции, когда эти два файла включены в один и тот же cpp. Этого можно избежать, используя полное имя везде в интерфейсе. На самом деле с функциями все намного сложнее. - person ltjax; 30.05.2011
comment
Я также искал способы сделать синтаксис более читаемым без слишком большого количества префикса std::. Я бы действительно хотел использовать using ::std::vector в моем собственном пространстве имен, прежде чем я определю свои классы, чтобы код читался легче. Я понимаю, что using namespace рискованно, и я могу понять проблему, если using объявить для функций. Но насколько я понимаю, единственная проблема с типами - это возможность конфликта, если кто-то еще должен определить тип с таким же именем в моем пространстве имен. Неужели это такая большая проблема, что никто не пользуется этим шаблоном? - person thomthom; 16.12.2013
comment
Итак, вы «создали» тип MyNamespace::vector. Само по себе это звучит достаточно безопасно. - person Will; 24.11.2017

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

person Björn Pollex    schedule 30.05.2011
comment
Спасибо, я думаю, это то, что я пытаюсь получить. По сути, я хочу, чтобы все векторы в этом пространстве имен были boost::numeric::ublas::vectors, чтобы любой файл .cpp, который включает этот заголовок и использует объявление using namespace MyNamespace;, использовал этот вектор вместо std::vector. Но не иначе. - person Brett Ryland; 30.05.2011
comment
@Brett: И если у них есть using namespace std;, у вас будет конфликт имен. Я всегда предпочитаю полностью определенные имена. Вы всегда можете создать короткий псевдоним для пространства имен. - person Björn Pollex; 30.05.2011
comment
Он не будет виден за пределами этого пространства имен. - хотя это правильно, обратного - нет: директиве using будет видно посторонний материал, потенциально изменяющий значение вашего и чужого кода. - person ltjax; 30.05.2011

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

person BЈовић    schedule 30.05.2011
comment
Но OP импортировал только один тип, а не все пространство имен. Это загрязняет окружающую среду? - person thomthom; 15.12.2013

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

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

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

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

За ваш комментарий от 30 мая 2011 г. в 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
}

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

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

  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 (безымянные пространства имен специально предназначены для избежания конфликты имен во время компоновки, что здесь неприменимо: это просто псевдоним времени компиляции). Хуже того, это вводит двусмысленность, если это имя уже существует!

person Nils von Barth    schedule 22.05.2016

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

person Puppy    schedule 30.05.2011