Пространства имён
Пространства имён позволяют предотвращать конфликты имён в крупных проектах.
Сущности, объявленные внутри блока пространства имён, помещаются в область видимости пространства имён, что предотвращает их ошибочное принятие за сущности с такими же именами в других областях видимости.
Сущности, объявленные вне всех блоков пространства имён, принадлежат глобальному пространству имён. Глобальное пространство имён принадлежит глобальной области видимости, и на него можно явно сослаться с помощью ведущего ::. Хотя у него нет объявления, глобальное пространство имён не является безымянным пространством имён.
Допускается несколько блоков пространства имён с одним и тем же именем. Все объявления внутри этих блоков объявляются в одной и той же области видимости пространства имён.
Синтаксис
namespace ns-name { declarations } | (1) | |
inline namespace ns-name { declarations } | (2) | (начиная с C++11) |
namespace { declarations } | (3) | |
ns-name :: member-name | (4) | |
using namespace ns-name ; | (5) | |
using ns-name :: member-name ; | (6) | |
namespace name = qualified-namespace ; | (7) | |
namespace ns-name :: member-name { declarations } | (8) | (начиная с C++17) |
namespace ns-name :: inline member-name { declarations } | (9) | (начиная с C++20) |
namespace A::B::C { ... } эквивалентно namespace A { namespace B { namespace C { ... } } }.namespace A::B::inline C { ... } эквивалентно namespace A::B { inline namespace C { ... } }. inline может стоять перед каждым именем пространства имён, кроме первого: namespace A::inline B::C {} эквивалентно namespace A { inline namespace B { namespace C {} } }.Пояснение
Пространства имён
inline(необязательно) namespace attr (необязательно) identifier { namespace-body } |
inline | - |
(начиная с C++11) если присутствует, делает пространство имён встроенным (см. ниже). Не может присутствовать в определении-расширении-пространства-имён, если в исходном-определении-пространства-имён не использовалось inline |
||
| attr | - | (начиная с C++17) необязательная последовательность из любого количества атрибутов | ||
| identifier | - | одно из следующего:
|
||
| namespace-body | - | возможно, пустая последовательность объявлений любого вида (включая определения классов и функций, а также вложенные пространства имён) |
Определения пространств имён допускаются только на уровне пространства имён, включая глобальный уровень.
Чтобы повторно открыть существующее пространство имён (то есть формально создать определение-расширение-пространства-имён), поиск identifier, используемого в определении пространства имён, должен разрешиться в имя пространства имён (а не псевдоним пространства имён), объявленное как член охватывающего пространства имён или встроенного пространства имён внутри охватывающего пространства имён.
namespace-body определяет область видимости пространства имён, влияющую на поиск имён.
Все имена, введённые объявлениями внутри namespace-body (включая определения вложенных пространств имён), становятся членами пространства имён identifier независимо от того, является ли это определение пространства имён исходным определением (введшим identifier) или определением-расширением (которое «повторно открыло» уже определённое пространство имён).
Член пространства имён, объявленный внутри тела пространства имён, можно определить или объявить повторно за его пределами с помощью явной квалификации
namespace Q
{
namespace V // V is a member of Q, and is fully defined within Q
{ // namespace Q::V { // C++17 alternative to the lines above
class C { void m(); }; // C is a member of V and is fully defined within V
// C::m is only declared
void f(); // f is a member of V, but is only declared here
}
void V::f() // definition of V's member f outside of V
// f's enclosing namespaces are still the global namespace, Q, and Q::V
{
extern void h(); // This declares ::Q::V::h
}
void V::C::m() // definition of V::C::m outside of the namespace (and the class body)
// enclosing namespaces are the global namespace, Q, and Q::V
{}
}
Определения и повторные объявления вне пространства имён допускаются только
- после точки объявления,
- на уровне пространства имён и
- в пространствах имён, охватывающих исходное пространство имён (включая глобальное пространство имён).
Кроме того, в них необходимо использовать синтаксис квалифицированного идентификатора.
namespace Q
{
namespace V // original-namespace-definition for V
{
void f(); // declaration of Q::V::f
}
void V::f() {} // OK
void V::g() {} // Error: g() is not yet a member of V
namespace V // extension-namespace-definition for V
{
void g(); // declaration of Q::V::g
}
}
namespace R // not an enclosing namespace for Q
{
void Q::V::g() {} // Error: cannot define Q::V::g inside R
}
void Q::V::g() {} // OK: global namespace encloses Q
Имена, введённые объявлениями friend внутри нелокального класса X, становятся членами самого внутреннего охватывающего пространства имён класса X, но не становятся видимыми для обычного поиска имён (ни неквалифицированного, ни квалифицированного), если только соответствующее объявление не предоставлено на уровне пространства имён до или после определения класса. Такое имя может быть найдено посредством ADL, который рассматривает и пространства имён, и классы.
При определении того, будет ли имя конфликтовать с ранее объявленным именем, такое объявление friend учитывает только самое внутреннее охватывающее пространство имён.
void h(int);
namespace A
{
class X
{
friend void f(X); // A::f is a friend
class Y
{
friend void g(); // A::g is a friend
friend void h(int); // A::h is a friend, no conflict with ::h
};
};
// A::f, A::g and A::h are not visible at namespace scope
// even though they are members of the namespace A
X x;
void g() // definition of A::g
{
f(x); // A::X::f is found through ADL
}
void f(X) {} // definition of A::f
void h(int) {} // definition of A::h
// A::f, A::g and A::h are now visible at namespace scope
// and they are also friends of A::X and A::X::Y
}
Встроенные пространства имёнВстроенное пространство имён — это пространство имён, в исходном-определении-пространства-имён которого используется необязательное ключевое слово Члены встроенного пространства имён во многих ситуациях (перечисленных ниже) рассматриваются как члены охватывающего пространства имён. Это свойство транзитивно: если пространство имён N содержит встроенное пространство имён M, которое, в свою очередь, содержит встроенное пространство имён O, члены O можно использовать так, как если бы они были членами M или N.
// in C++14, std::literals and its member namespaces are inline
{
using namespace std::string_literals; // makes visible operator""s
// from std::literals::string_literals
auto str = "abc"s;
}
{
using namespace std::literals; // makes visible both
// std::literals::string_literals::operator""s
// and std::literals::chrono_literals::operator""s
auto str = "abc"s;
auto min = 60s;
}
{
using std::operator""s; // makes both std::literals::string_literals::operator""s
// and std::literals::chrono_literals::operator""s visible
auto str = "abc"s;
auto min = 60s;
}
Примечание: правило о специализациях позволяет версионировать библиотеки: разные реализации шаблона библиотеки могут быть определены в разных встроенных пространствах имён, при этом пользователь по-прежнему может расширить родительское пространство имён, явно специализировав первичный шаблон: namespace Lib
{
inline namespace Lib_1
{
template<typename T> class A;
}
template<typename T> void g(T) { /* ... */ }
}
/* ... */
struct MyClass { /* ... */ };
namespace Lib
{
template<> class A<MyClass> { /* ... */ };
}
int main()
{
Lib::A<MyClass> a;
g(a); // ok, Lib is an associated namespace of A
}
| (начиная с C++11) |
Безымянные пространства имён
Определение-безымянного-пространства-имён — это определение пространства имён следующего вида
inline(необязательно) namespace attr (необязательно) { namespace-body } |
inline | - | (начиная с C++11) если присутствует, делает пространство имён встроенным |
| attr | - | (начиная с C++17) необязательная последовательность из любого количества атрибутов |
Это определение рассматривается как определение пространства имён с уникальным именем и директивы using в текущей области видимости, указывающей на это безымянное пространство имён (Примечание: неявно добавленная директива using делает пространство имён доступным для квалифицированного поиска имён и неквалифицированного поиска имён, но не для поиска, зависящего от аргументов). Уникальное имя является уникальным во всей программе, но в пределах одной единицы трансляции каждому определению безымянного пространства имён соответствует одно и то же уникальное имя: несколько определений безымянного пространства имён в одной области видимости обозначают одно и то же безымянное пространство имён.
namespace
{
int i; // defines ::(unique)::i
}
void f()
{
i++; // increments ::(unique)::i
}
namespace A
{
namespace
{
int i; // A::(unique)::i
int j; // A::(unique)::j
}
void g() { i++; } // A::(unique)::i++
}
using namespace A; // introduces all names from A into global namespace
void h()
{
i++; // error: ::(unique)::i and ::A::(unique)::i are both in scope
A::i++; // ok, increments ::A::(unique)::i
j++; // ok, increments ::A::(unique)::j
}
| Хотя имена в безымянном пространстве имён могут быть объявлены с внешним связыванием, они никогда не доступны из других единиц трансляции, поскольку имя их пространства имён уникально. | (до C++11) |
| Безымянные пространства имён, а также все пространства имён, объявленные прямо или косвенно внутри безымянного пространства имён, имеют внутреннее связывание, то есть любое имя, объявленное внутри безымянного пространства имён, имеет внутреннее связывание. | (начиная с C++11) |
Объявления using
Вводит имя, определённое в другом месте, в область объявления, где встречается это объявление using.
using typename(необязательно) nested-name-specifier unqualified-id ; | (до C++17) | |
using declarator-list ; | (начиная с C++17) |
typename | - | ключевое слово typename можно использовать при необходимости для разрешения зависимых имён, когда объявление using вводит тип-член из базового класса в шаблон класса |
| nested-name-specifier | - | последовательность имён и операторов разрешения области видимости ::, заканчивающаяся оператором разрешения области видимости. Одиночный :: обозначает глобальное пространство имён. |
| unqualified-id | - | выражение-id |
| declarator-list | - | список из одного или более объявителей, разделённых запятыми, вида typename(необязательно) nested-name-specifier unqualified-id. После объявителя может следовать многоточие, обозначающее раскрытие пакета, хотя эта форма имеет смысл только в определениях производных классов |
Объявления using можно использовать для введения членов пространства имён в другие пространства имён и области видимости блоков, а также для введения членов базового класса в определения производных классов или для введения перечислителей в области видимости пространств имён, блоков и классов(начиная с C++20).
| Объявление using с несколькими using-объявителями эквивалентно соответствующей последовательности объявлений using, каждое из которых содержит один using-объявитель. | (начиная с C++17) |
О применении в определениях производных классов см. объявление using.
Имена, введённые в область видимости пространства имён с помощью объявления using, можно использовать как любые другие имена, в том числе выполнять их квалифицированный поиск из других областей видимости:
void f();
namespace A
{
void g();
}
namespace X
{
using ::f; // global f is now visible as ::X::f
using A::g; // A::g is now visible as ::X::g
using A::g, A::g; // (C++17) OK: double declaration allowed at namespace scope
}
void h()
{
X::f(); // calls ::f
X::g(); // calls A::g
}
Если после использования объявления using для введения члена из пространства имён это пространство имён расширяется и в него добавляются другие объявления с тем же именем, эти дополнительные объявления не становятся видимыми через объявление using (в отличие от директивы using). Исключение составляет случай, когда объявление using указывает на шаблон класса: частичные специализации, введённые позднее, фактически видимы, поскольку их поиск выполняется через первичный шаблон.
namespace A
{
void f(int);
}
using A::f; // ::f is now a synonym for A::f(int)
namespace A // namespace extension
{
void f(char); // does not change what ::f means
}
void foo()
{
f('a'); // calls f(int), even though f(char) exists.
}
void bar()
{
using A::f; // this f is a synonym for both A::f(int) and A::f(char)
f('a'); // calls f(char)
}
Объявления using не могут указывать на идентификатор шаблона, пространство имён или перечислитель области видимости(до C++20). Каждое объявление в объявлении using вводит ровно одно имя; например, объявление using для перечисления не вводит ни один из его перечислителей.
К объявлениям using применяются все ограничения на обычные объявления тех же имён, правила сокрытия и перегрузки:
namespace A
{
int x;
}
namespace B
{
int i;
struct g {};
struct x {};
void f(int);
void f(double);
void g(char); // OK: function name g hides struct g
}
void func()
{
int i;
using B::i; // error: i declared twice
void f(char);
using B::f; // OK: f(char), f(int), f(double) are overloads
f(3.5); // calls B::f(double)
using B::g;
g('a'); // calls B::g(char)
struct g g1; // declares g1 to have type struct B::g
using B::x;
using A::x; // OK: hides struct B::x
x = 99; // assigns to A::x
struct x x1; // declares x1 to have type struct B::x
}
Если функция была введена объявлением using, объявление функции с тем же именем и списком параметров некорректно (если только объявление не относится к той же функции). Если шаблон функции был введён объявлением using, объявление шаблона функции с тем же именем, списком типов параметров, возвращаемым типом и списком параметров шаблона некорректно. Два объявления using могут вводить функции с одинаковыми именами и списками параметров, но если предпринята попытка вызвать такую функцию, программа некорректна.
namespace B
{
void f(int);
void f(double);
}
namespace C
{
void f(int);
void f(double);
void f(char);
}
void h()
{
using B::f; // introduces B::f(int), B::f(double)
using C::f; // introduces C::f(int), C::f(double), and C::f(char)
f('h'); // calls C::f(char)
f(1); // error: B::f(int) or C::f(int)?
void f(int); // error: f(int) conflicts with C::f(int) and B::f(int)
}
Если сущность объявлена, но не определена во внутреннем пространстве имён, затем объявлена в наружном пространстве имён посредством объявления using, а после этого в наружном пространстве имён появляется определение с тем же неквалифицированным именем, это определение является членом наружного пространства имён и конфликтует с объявлением using:
namespace X
{
namespace M
{
void g(); // declares, but doesn't define X::M::g()
}
using M::g;
void g(); // Error: attempt to declare X::g which conflicts with X::M::g()
}
В более общем случае объявление, встречающееся на уровне любого пространства имён и вводящее имя с помощью неквалифицированного идентификатора, всегда вводит член в то пространство имён, в котором оно находится, а не в какое-либо другое пространство имён. Исключениями являются явные инстанцирования и явные специализации первичного шаблона, определённого во встроенном пространстве имён: поскольку они не вводят новое имя, в охватывающем пространстве имён для них можно использовать неквалифицированный идентификатор.
Директивы using
Директива using — это объявление блока следующего синтаксиса:
attr (необязательно) using namespace namespace-name ; | (1) | |
attr (необязательно) using namespace splice-specifier ; | (2) | (начиная с C++26) |
| attr | - | (начиная с C++11) любое количество атрибутов, применяемых к этой директиве using |
| namespace-name | - | имя пространства имён (возможно, квалифицированное). При поиске этого имени учитываются только объявления пространств имён. Оно не должно содержать зависимый спецификатор splice(начиная с C++26) |
| splice-specifier | - | спецификатор splice, который не должен быть зависимым |
Директивы using допускаются только на уровне области видимости пространства имён и в области видимости блока. При неквалифицированном поиске имени, следующего за директивой using и до конца области видимости, в которой она встречается, каждое имя из namespace-name или splice-specifier(начиная с C++26) видно так, как если бы оно было объявлено в ближайшем охватывающем пространстве имён, которое содержит и директиву using, и namespace-name или splice-specifier(начиная с C++26).
Директива using не добавляет никаких имён в область объявления, где она встречается (в отличие от объявления using), и потому не препятствует объявлению имён с теми же именами.
Для целей неквалифицированного поиска директивы using транзитивны: если область видимости содержит директиву using, указывающую на namespace-name, внутри которого, в свою очередь, есть директива using для некоторого namespace-name-2, эффект будет таким, как если бы директивы using из второго пространства имён находились в первом. Порядок следования этих транзитивно включённых пространств имён не влияет на поиск имён.
namespace A
{
int i;
}
namespace B
{
int i;
int j;
namespace C
{
namespace D
{
using namespace A;
// Names from A are "injected" into D.
// Unqualified lookup within D considers these names to have the same
// scope as the global scope (e.g. for the purposes of name hiding).
// Qualified lookup referring to D (D::name for some name)
// will find the same name as unqualified lookup within D.
int j;
int k;
int a = i; // i is B::i, because A::i is hidden by B::i
int b = ::i; // error: there is still no i in the global namespace
}
using namespace D; // names from D and A are injected into C
int k = 89; // OK to declare name identical to one introduced by a using
int l = k; // ambiguous: C::k or D::k
int m = i; // ok: B::i hides A::i
int n = j; // ok: D::j hides B::j
}
}
// These are all equivalent definitions:
int t0 = B::i;
int t1 = B::C::a;
int t2 = B::C::D::a;
Если после использования директивы using для указания на пространство имён это пространство имён расширяется и в него добавляются новые члены и/или директивы using, эти дополнительные члены и пространства имён становятся видимыми через директиву using (в отличие от объявления using).
namespace D
{
int d1;
void f(char);
}
using namespace D; // introduces D::d1, D::f, D::d2, D::f,
// E::e, and E::f into global namespace!
int d1; // OK: no conflict with D::d1 when declaring
namespace E
{
int e;
void f(int);
}
namespace D // namespace extension
{
int d2;
using namespace E; // transitive using-directive
void f(int);
}
void f()
{
d1++; // error: ambiguous ::d1 or D::d1?
::d1++; // OK
D::d1++; // OK
d2++; // OK, d2 is D::d2
e++; // OK: e is E::e due to transitive using
f(1); // error: ambiguous: D::f(int) or E::f(int)?
f('a'); // OK: the only f(char) is D::f(char)
}
Примечания
Директива using using namespace std; на любом уровне пространства имён вводит каждое имя из пространства имён std в глобальное пространство имён (поскольку глобальное пространство имён является ближайшим пространством имён, содержащим и std, и любое объявленное пользователем пространство имён), что может привести к нежелательным конфликтам имён. Это и другие директивы using обычно считаются плохой практикой на уровне файла в заголовочном файле (SF.7: не пишите using namespace на глобальном уровне в заголовочном файле).
| Макрос проверки поддержки возможностей | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_namespace_attributes | 201411L | (C++17) | Атрибуты пространств имён |
Ключевые слова
Пример
В этом примере показано, как использовать пространство имён для создания класса, имя которого уже использовано в пространстве имён std.
#include <vector>
namespace vec
{
template<typename T>
class vector
{
// ...
};
} // of vec
int main()
{
std::vector<int> v1; // Standard vector.
vec::vector<int> v2; // User defined vector.
// v1 = v2; // Error: v1 and v2 are different object's type.
{
using namespace std;
vector<int> v3; // Same as std::vector
v1 = v3; // OK
}
{
using vec::vector;
vector<int> v4; // Same as vec::vector
v2 = v4; // OK
}
}
Отчёты об ошибках
Следующие отчёты об ошибках, изменяющие поведение, были ретроактивно применены к ранее опубликованным стандартам C++.
| DR | Применён к | Поведение согласно опубликованному тексту | Правильное поведение |
|---|---|---|---|
| CWG 101 | C++98 | программа некорректна, если объявление функции на уровне пространства имён или блока и функция, введённая объявлением using, объявляют одну и ту же функцию (неоднозначности нет) | разрешено |
| CWG 373 | C++98 | поиск учитывал только объявления пространств имён для последнего имени в операнде директивы using (что неоптимально, поскольку классы не могут содержать пространства имён) | ограничение поиска применяется ко всем именам в операндах директив using |
| CWG 460 | C++98 | объявление using могло указывать на пространство имён | запрещено |
| CWG 565 | C++98 | объявление using не может вводить функцию, идентичную другой функции в той же области видимости, но это ограничение не распространялось на шаблоны функций | применить такое же ограничение и к шаблонам функций |
| CWG 986 | C++98 | директива using была транзитивной для квалифицированного поиска | транзитивность только для неквалифицированного поиска |
| CWG 987 | C++98 | сущности, объявленные во вложенном пространстве имён, также являлись членами охватывающего пространства имён | вложенные области исключены |
| CWG 1021 | C++98 | было неясно, считается ли сущность, определение которой введено в пространство имён через объявление using, определённой в этом пространстве имён | не определена в этом пространстве имён |
| CWG 1838 | C++98 | неквалифицированное определение во внешнем пространстве имён могло определять сущность, объявленную, но не определённую в другом пространстве имён и введённую с помощью using | неквалифицированное определение всегда относится к своему пространству имён |
| CWG 2155 | C++98 | решение проблемы CWG 1838 не применялось к объявлениям классов и перечислений | применено |
См. также
| псевдоним пространства имён | создаёт псевдоним существующего пространства имён |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/namespace