C++
Пространства имен
Поиск…
Вступление
Используется для предотвращения конфликтов имен при использовании нескольких библиотек, пространство имен является декларативным префиксом для функций, классов, типов и т. Д.
Синтаксис
- Идентификатор пространства имен ( opt ) { declaration-seq }
- inline namespace identifier ( opt ) { declaration-seq } / *, поскольку C ++ 11 * /
- inline ( opt ) namespace attribute-specifier-seq identifier ( opt ) { declaration-seq } / *, поскольку C ++ 17 * /
- namespace enclosing-namespace-specifier :: identifier { declaration-seq } / *, поскольку C ++ 17 * /
- Идентификатор пространства имен = квалифицированный-namespace-specifier ;
- использование namespace -name-specifier ( opt ) namespace-name ;
- attribute-specifier-seq using namespace имя-имя-спецификатор ( opt ) namespace-name ; / *, поскольку C ++ 11 * /
замечания
Пространство namespace
ключевых слов имеет три разных значения в зависимости от контекста:
Когда за ним следует необязательное имя и последовательность объявлений, заключенная в фигурной скобке, он определяет новое пространство имен или расширяет существующее пространство имен с этими объявлениями. Если имя опущено, пространство имен является безымянным пространством имен .
Когда за ним следует имя и знак равенства, он объявляет псевдоним пространства имен .
Когда ему предшествует
using
и имя имени пространства имен, оно формирует директиву using , которая позволяет найти имена в данном пространстве имен путем поиска неквалифицированного имени (но не обновляет эти имена в текущей области). Директива-директива не может возникать в классе.
using namespace std;
не рекомендуется. Зачем? Потому что namespace std
огромно! Это означает, что существует вероятность того, что имена будут сталкиваться:
//Really bad!
using namespace std;
//Calculates p^e and outputs it to std::cout
void pow(double p, double e) { /*...*/ }
//Calls pow
pow(5.0, 2.0); //Error! There is already a pow function in namespace std with the same signature,
//so the call is ambiguous
Что такое пространства имен?
Пространство имен C ++ представляет собой набор объектов C ++ (функции, классы, переменные), имена которых имеют префикс имени пространства имен. При написании кода в пространстве имен именованные объекты, принадлежащие к этому пространству имен, не обязательно должны иметь префикс имени пространства имен, но сущности вне него должны использовать полное имя. Полноценное имя имеет формат <namespace>::<entity>
. Пример:
namespace Example
{
const int test = 5;
const int test2 = test + 12; //Works within `Example` namespace
}
const int test3 = test + 3; //Fails; `test` not found outside of namespace.
const int test3 = Example::test + 3; //Works; fully qualified name used.
Пространства имен полезны для группировки связанных определений вместе. Пройдите аналогию с торговым центром. Как правило, торговый центр делится на несколько магазинов, каждый магазин продает предметы из определенной категории. Один магазин может продавать электронику, а другой магазин может продавать обувь. Эти логические разделения в типах магазинов помогают покупателям находить предметы, которые они ищут. Пространства имен помогают программистам на С ++, например покупателям, находить функции, классы и переменные, которые они ищут, организуя их логически. Пример:
namespace Electronics
{
int TotalStock;
class Headphones
{
// Description of a Headphone (color, brand, model number, etc.)
};
class Television
{
// Description of a Television (color, brand, model number, etc.)
};
}
namespace Shoes
{
int TotalStock;
class Sandal
{
// Description of a Sandal (color, brand, model number, etc.)
};
class Slipper
{
// Description of a Slipper (color, brand, model number, etc.)
};
}
Существует одно пространство имен предопределен, что глобальное пространство имен, не имеет имени, но можно обозначить ::
. Пример:
void bar() {
// defined in global namespace
}
namespace foo {
void bar() {
// defined in namespace foo
}
void barbar() {
bar(); // calls foo::bar()
::bar(); // calls bar() defined in global namespace
}
}
Создание пространств имен
Создание пространства имен очень просто:
//Creates namespace foo
namespace Foo
{
//Declares function bar in namespace foo
void bar() {}
}
Чтобы вызвать bar
, вы должны сначала указать пространство имен, а затем оператор разрешения области ::
Foo::bar();
Разрешено создавать одно пространство имен в другом, например:
namespace A
{
namespace B
{
namespace C
{
void bar() {}
}
}
}
Вышеприведенный код можно упростить до следующего:
namespace A::B::C
{
void bar() {}
}
Расширение пространств имен
Полезной функцией namespace
s является то, что вы можете расширять их (добавлять к ней участников).
namespace Foo
{
void bar() {}
}
//some other stuff
namespace Foo
{
void bar2() {}
}
Использование директивы
Ключевое слово «использование» имеет три варианта. В сочетании с ключевым словом «namespace» вы пишете «using directive»:
Если вы не хотите писать Foo::
перед каждым материалом в пространстве имен Foo
, вы можете использовать using namespace Foo;
импортировать каждую вещь из Foo
.
namespace Foo
{
void bar() {}
void baz() {}
}
//Have to use Foo::bar()
Foo::bar();
//Import Foo
using namespace Foo;
bar(); //OK
baz(); //OK
Также можно импортировать выбранные объекты в пространстве имен, а не во всем пространстве имен:
using Foo::bar;
bar(); //OK, was specifically imported
baz(); // Not OK, was not imported
Предупреждение: using namespace
s в файлах заголовков в большинстве случаев рассматривается как плохой стиль. Если это будет сделано, пространство имен будет импортировано в каждый файл, включающий заголовок. Поскольку нет способа «не using
» пространство имен, это может привести к загрязнению пространства имен (больше или неожиданным символам в глобальном пространстве имен) или, что еще хуже, конфликтам. См. Этот пример для иллюстрации проблемы:
/***** foo.h *****/
namespace Foo
{
class C;
}
/***** bar.h *****/
namespace Bar
{
class C;
}
/***** baz.h *****/
#include "foo.h"
using namespace Foo;
/***** main.cpp *****/
#include "bar.h"
#include "baz.h"
using namespace Bar;
C c; // error: Ambiguity between Bar::C and Foo::C
Директива-директива не может возникать в классе.
Поиск зависимых аргументов
При вызове функции без явного определителя пространства имен компилятор может выбрать вызов функции в пространстве имен, если один из типов параметров этой функции также находится в этом пространстве имен. Это называется «зависимым от аргумента» или ADL:
namespace Test
{
int call(int i);
class SomeClass {...};
int call_too(const SomeClass &data);
}
call(5); //Fails. Not a qualified function name.
Test::SomeClass data;
call_too(data); //Succeeds
call
невозможен, потому что ни один из его типов параметров не приходит из пространства имен Test
. call_too
работает, потому что SomeClass
является членом Test
и поэтому он подходит для правил ADL.
Когда ADL не возникает
ADL не возникает, если обычный неквалифицированный поиск находит член класса, функцию, объявленную в области блока, или что-то, что не относится к типу функции. Например:
void foo();
namespace N {
struct X {};
void foo(X ) { std::cout << '1'; }
void qux(X ) { std::cout << '2'; }
}
struct C {
void foo() {}
void bar() {
foo(N::X{}); // error: ADL is disabled and C::foo() takes no arguments
}
};
void bar() {
extern void foo(); // redeclares ::foo
foo(N::X{}); // error: ADL is disabled and ::foo() doesn't take any arguments
}
int qux;
void baz() {
qux(N::X{}); // error: variable declaration disables ADL for "qux"
}
Встроенное пространство имен
inline namespace
включает содержимое встроенного пространства имен в охватывающем пространстве имен, поэтому
namespace Outer
{
inline namespace Inner
{
void foo();
}
}
в основном эквивалентен
namespace Outer
{
namespace Inner
{
void foo();
}
using Inner::foo;
}
но элемент из Outer::Inner::
и те, которые связаны с Outer::
, идентичны.
Итак, следующее эквивалентно
Outer::foo();
Outer::Inner::foo();
Альтернатива using namespace Inner;
не будет эквивалентен некоторым сложным частям в качестве типовой специализации:
За
#include <outer.h> // See below
class MyCustomType;
namespace Outer
{
template <>
void foo<MyCustomType>() { std::cout << "Specialization"; }
}
Встроенное пространство имен позволяет специализировать
Outer::foo
// outer.h // include guard omitted for simplification namespace Outer { inline namespace Inner { template <typename T> void foo() { std::cout << "Generic"; } } }
В то время как
using namespace
не позволяет специализацииOuter::foo
// outer.h // include guard omitted for simplification namespace Outer { namespace Inner { template <typename T> void foo() { std::cout << "Generic"; } } using namespace Inner; // Specialization of `Outer::foo` is not possible // it should be `Outer::Inner::foo`. }
Встроенное пространство имен - это способ позволить нескольким версиям сожительствовать и дефолтировать inline
namespace MyNamespace
{
// Inline the last version
inline namespace Version2
{
void foo(); // New version
void bar();
}
namespace Version1 // The old one
{
void foo();
}
}
И с использованием
MyNamespace::Version1::foo(); // old version
MyNamespace::Version2::foo(); // new version
MyNamespace::foo(); // default version : MyNamespace::Version1::foo();
Без имени / анонимных пространств имен
Неименованное пространство имен может использоваться, чтобы гарантировать, что имена имеют внутреннюю связь (на которые может ссылаться только текущая единица перевода). Такое пространство имен определяется так же, как и любое другое пространство имен, но без имени:
namespace {
int foo = 42;
}
foo
отображается только в блоке перевода, в котором он отображается.
Рекомендуется никогда не использовать неназванные пространства имен в заголовочных файлах, так как это дает версию содержимого для каждой единицы перевода, в которую она включена. Это особенно важно, если вы определяете неконтекстные глобальные переменные.
// foo.h
namespace {
std::string globalString;
}
// 1.cpp
#include "foo.h" //< Generates unnamed_namespace{1.cpp}::globalString ...
globalString = "Initialize";
// 2.cpp
#include "foo.h" //< Generates unnamed_namespace{2.cpp}::globalString ...
std::cout << globalString; //< Will always print the empty string
Компактные вложенные пространства имен
namespace a {
namespace b {
template<class T>
struct qualifies : std::false_type {};
}
}
namespace other {
struct bob {};
}
namespace a::b {
template<>
struct qualifies<::other::bob> : std::true_type {};
}
Вы можете ввести оба пространства имен a
и b
за один шаг с namespace a::b
начиная с C ++ 17.
Слияние длинного пространства имен
Обычно это используется для переименования или сокращения длинных ссылок пространства имен, например, для ссылок на компоненты библиотеки.
namespace boost
{
namespace multiprecision
{
class Number ...
}
}
namespace Name1 = boost::multiprecision;
// Both Type declarations are equivalent
boost::multiprecision::Number X // Writing the full namespace path, longer
Name1::Number Y // using the name alias, shorter
Область объявления псевдонима
Алиас Декларация затрагивает предшествующими используя операторы
namespace boost
{
namespace multiprecision
{
class Number ...
}
}
using namespace boost;
// Both Namespace are equivalent
namespace Name1 = boost::multiprecision;
namespace Name2 = multiprecision;
Тем не менее, легче запутаться в том, какое пространство имен вы используете, когда у вас есть что-то вроде этого:
namespace boost
{
namespace multiprecision
{
class Number ...
}
}
namespace numeric
{
namespace multiprecision
{
class Number ...
}
}
using namespace numeric;
using namespace boost;
// Not recommended as
// its not explicitly clear whether Name1 refers to
// numeric::multiprecision or boost::multiprecision
namespace Name1 = multiprecision;
// For clarity, its recommended to use absolute paths
// instead
namespace Name2 = numeric::multiprecision;
namespace Name3 = boost::multiprecision;
Псевдоним пространства имен
Пространству имен может быть присвоен псевдоним ( то есть другое имя для того же пространства имен) с использованием идентификатора namespace
=
синтаксис. К элементам пространства имен с псевдонимом можно получить доступ, присвоив им имя псевдонима. В следующем примере вложенное пространство имен AReallyLongName::AnotherReallyLongName
неудобно вводить, поэтому функция qux
локально объявляет псевдоним N
После этого члены этого пространства имен могут быть доступны просто с помощью N::
.
namespace AReallyLongName {
namespace AnotherReallyLongName {
int foo();
int bar();
void baz(int x, int y);
}
}
void qux() {
namespace N = AReallyLongName::AnotherReallyLongName;
N::baz(N::foo(), N::bar());
}