Пространства имён
Пространства имён предоставляют способ предотвращения конфликтов имён в крупных проектах.
Сущности, объявленные внутри блока пространства имён, помещаются в область видимости пространства имён, что предотвращает их ошибочное восприятие как идентичных сущностей в других областях видимости.
Сущности, объявленные вне всех блоков пространства имён, относятся к глобальному пространству имён. Глобальное пространство имён относится к глобальной области видимости и может быть явно указано с ведущим ::. Хотя у него нет объявления, глобальное пространство имён не является безымянным пространством имён.
Разрешается несколько блоков пространства имён с одинаковым именем. Все объявления внутри этих блоков объявляются в одной и той же области видимости пространства имён.
Синтаксис
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), если присутствует, делает это вложенным пространством имён (см. ниже). Не может появляться в extension-namespace-definition, если original-namespace-definition не использовало inline |
||
| attr | - | (с C++17) необязательная последовательность любого количества атрибутов | ||
| identifier | - | либо
|
||
| namespace-body | - | , возможно, пустая последовательность объявлений любого типа (включая определения классов и функций, а также вложенных пространств имён) |
Определения пространств имён разрешены только в области видимости пространства имён, включая глобальную область видимости.
Чтобы повторно открыть существующее пространство имён (формально, чтобы быть extension-namespace-definition), поиск identifier, используемого в определении пространства имён, должен привести к имени пространства имён (а не псевдониму пространства имён), которое было объявлено в качестве члена содержащего пространства имён или вложенного inline пространства имён внутри содержащего пространства имён.
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
{}
}Определения и повторные объявления вне пространства имён разрешены
- после точки объявления,
- в области видимости пространства имён, и
- в пространствах имён, которые содержат исходное пространство имён (включая глобальное пространство имён).
Кроме того, они должны использовать синтаксис qualified-id.
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 | - | выражение-идентификатор |
| 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 nested-name-specifier (необязательно) namespace-name ; | (1) |
| attr | - | (с C++11) любое количество атрибутов, которые применяются к этому using-направлению |
| nested-name-specifier | - | последовательность имен и операторов разрешения области видимости ::, заканчивающаяся оператором разрешения области видимости. Одиночное :: ссылается на глобальное пространство имён. При поиске имен в этой последовательности, поиск учитывает только объявления пространств имён |
| namespace-name | - | имя пространства имён. При поиске этого имени, поиск учитывает только объявления пространств имён |
Using-направления разрешены только в пространстве имён области видимости и в блочной области видимости. С точки зрения неквалифицированного поиска имён любого имени после using-направления и до конца области видимости, в которой оно появляется, каждое имя из namespace-name является видимым, как если бы оно было объявлено в ближайшем окружающем пространстве имён, которое содержит как using-направление, так и namespace-name.
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; // all names from A injected into global namespace
int j;
int k;
int a = i; // i is B::i, because A::i is hidden by B::i
}
using namespace D; // names from D are injected into C
// names from A are injected into global namespace
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
}
}Если после использования 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 в глобальной области видимости в файле заголовков).
| Макрос проверки функций | Значение | Std | Функция |
|---|---|---|---|
__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 issue 1838 не применялось к объявлениям классов и перечислений | применено |
См. также
| псевдоним пространства имён | создаёт псевдоним существующего пространства имён |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/namespace