Namespaces
Variants

Namespaces

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Namespace declaration
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Пространства имён предоставляют метод предотвращения конфликтов имён в крупных проектах.

Объявления сущностей внутри блока пространства имён помещаются в область видимости пространства имён, что предотвращает их ошибочное принятие за одноимённые сущности в других областях видимости.

Объявления, находящиеся вне всех блоков пространств имён, принадлежат глобальному пространству имён . Глобальное пространство имён принадлежит глобальной области видимости и может быть явно указано с помощью ведущего :: . Хотя у него нет объявления, глобальное пространство имён не является неименованным пространством имён .

Допускается несколько блоков пространства имён с одинаковым именем. Все объявления внутри этих блоков объявляются в одной и той же области видимости пространства имён.

Содержание

Синтаксис

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)
2) Определение встроенного пространства имён для пространства имён ns-name . Объявления внутри ns-name будут видны в его охватывающем пространстве имён.
3) Определение безымянного пространства имен . Его члены имеют потенциальную область видимости от точки их объявления до конца единицы трансляции и обладают внутренним связыванием .
4) Имена пространств имён (наряду с именами классов) могут появляться в левой части оператора разрешения области видимости, как часть квалифицированного поиска имён .
5) using-директива : С точки зрения неполного поиска имени любого имени после using-директивы и до конца области видимости, в которой она находится, каждое имя из ns-name видимо так, как если бы оно было объявлено в ближайшем охватывающем пространстве имён, которое содержит как using-директиву, так и ns-name .
6) using-объявление : делает символ member-name из пространства имён ns-name доступным для неполного поиска как если бы он был объявлен в той же области видимости класса, блока или пространства имён, где появляется это using-объявление.
namespace-alias-definition : делает name синонимом другого пространства имен: смотрите namespace alias
8) определение вложенного пространства имён: namespace A :: B :: C { ... } эквивалентно namespace A { namespace B { namespace C { ... } } } .
9) вложенное определение встроенного пространства имён: 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 - (since C++11) если присутствует, делает это встроенным пространством имен (см. ниже). Не может появляться в extension-namespace-definition если original-namespace-definition не использовал inline
attr - (since C++17) опциональная последовательность любого количества attributes
identifier - либо
  • ранее не использованный идентификатор, в этом случае это original-namespace-definition ;
  • имя пространства имен, в этом случае это extension-namespace-definition ;
  • последовательность спецификаторов охватывающих пространств имен, разделенных :: , заканчивающаяся identifier , в этом случае это nested-namespace-definition
(since C++17)
namespace-body - возможно пустая последовательность declarations любого вида (включая определения классов и функций, а также вложенные пространства имен)

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

Чтобы повторно открыть существующее пространство имён (формально, чтобы быть определением-расширением-пространства-имён ), поиск идентификатора , используемого в определении пространства имён, должен разрешаться в имя пространства имён (не псевдоним пространства имён), которое было объявлено как член охватывающего пространства имён или встроенного пространства имён внутри охватывающего пространства имён.

Тело пространства имён namespace-body определяет область видимости пространства имён , которая влияет на поиск имён .

Все имена, введённые объявлениями, которые появляются внутри namespace-body (включая вложенные определения пространств имён), становятся членами пространства имён identifier , независимо от того, является ли это определение пространства имён оригинальным определением (которое ввело identifier ) или расширяющим определением пространства имён (которое "переоткрыло" уже определённое пространство имён)

Член пространства имен, объявленный внутри тела пространства имен, может быть определен или переобъявлен вне его с использованием явной квалификации

namespace Q
{
    namespace V   // V является членом Q и полностью определен внутри Q
    { // namespace Q::V { // C++17 альтернатива строкам выше
        class C { void m(); }; // C является членом V и полностью определен внутри V
                               // C::m только объявлен
        void f(); // f является членом V, но только объявлен здесь
    }
    void V::f() // определение члена f пространства имен V вне V
                // охватывающие пространства имен f все еще глобальное пространство имен, Q и Q::V
    {
        extern void h(); // Это объявляет ::Q::V::h
    }
    void V::C::m() // определение V::C::m вне пространства имен (и тела класса)
                   // охватывающие пространства имен - глобальное пространство имен, Q и Q::V
    {}
}

Определения и переобъявления вне пространства имен разрешены только

  • после точки объявления,
  • на уровне пространства имен, и
  • в пространствах имен, охватывающих исходное пространство имен (включая глобальное пространство имен).

Кроме того, они должны использовать синтаксис qualified-id.

namespace Q
{
    namespace V    // исходное определение пространства имен для V
    {
        void f();  // объявление Q::V::f
    }
    void V::f() {} // OK
    void V::g() {} // Ошибка: g() еще не является членом V
    namespace V    // расширенное определение пространства имен для V
    {
        void g();  // объявление Q::V::g
    }
}
namespace R           // не является охватывающим пространством имен для Q
{
    void Q::V::g() {} // Ошибка: нельзя определить Q::V::g внутри R
}
void Q::V::g() {}     // OK: глобальное пространство имен охватывает Q

Имена, введённые с помощью friend объявлений внутри нелокального класса X, становятся членами ближайшего охватывающего пространства имён X, но они не становятся видимыми для обычного name lookup (ни unqualified , ни qualified ), если соответствующее объявление не предоставлено на уровне пространства имён, либо до, либо после определения класса. Такое имя может быть найдено с помощью ADL , которое учитывает как пространства имён, так и классы.

Только самая внутренняя охватывающая пространство имен учитывается таким объявлением дружественной функции при решении вопроса о том, будет ли имя конфликтовать с ранее объявленным именем.

void h(int);
namespace A
{
    class X
    {
        friend void f(X);       // A::f является дружественной функцией
        class Y
        {
            friend void g();    // A::g является дружественной функцией
            friend void h(int); // A::h является дружественной функцией, нет конфликта с ::h
        };
    };
    // A::f, A::g и A::h не видны в области видимости пространства имён
    // несмотря на то, что они являются членами пространства имён A
    X x;
    void g()  // определение A::g
    {
        f(x); // A::X::f находится через ADL
    }
    void f(X) {}   // определение A::f
    void h(int) {} // определение A::h
    // A::f, A::g и A::h теперь видны в области видимости пространства имён
    // и также являются дружественными функциями для A::X и A::X::Y
}

Встроенные пространства имен

Встроенное пространство имен — это пространство имен, которое использует необязательное ключевое слово inline в своем original-namespace-definition .

Члены встроенного пространства имен во многих ситуациях (перечисленных ниже) рассматриваются как члены охватывающего пространства имен. Это свойство транзитивно: если пространство имен N содержит встроенное пространство имен M, которое в свою очередь содержит встроенное пространство имен O, то члены O могут использоваться так, как если бы они были членами M или N.

  • using-directive , которая именует встроенное пространство имен, неявно вставляется в охватывающее пространство имен (аналогично неявной using-directive для безымянного пространства имен)
  • В поиске, зависимом от аргументов , когда пространство имен добавляется в набор ассоциированных пространств имен, его встроенные пространства имен также добавляются, и если встроенное пространство имен добавляется в список ассоциированных пространств имен, его охватывающее пространство имен также добавляется.
  • Каждый член встроенного пространства имен может быть частично специализирован, явно инстанцирован или явно специализирован, как если бы он был членом охватывающего пространства имен.
  • Квалифицированный поиск имен , который исследует охватывающее пространство имен, будет включать имена из встроенных пространств имен, даже если то же имя присутствует в охватывающем пространстве имен.
// 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)

Безымянные пространства имён

unnamed-namespace-definition — это определение пространства имен вида

inline (необязательно) namespace attr  (необязательно) { namespace-body }
inline - (since C++11) если присутствует, делает это встроенным пространством имён
attr - (since C++17) опциональная последовательность любого количества атрибутов

Это определение трактуется как определение пространства имён с уникальным именем и using-директивой в текущей области видимости, которая указывает на это безымянное пространство имён (Примечание: неявно добавленная using-директива делает пространство имён доступным для qualified name lookup и unqualified name lookup , но не для argument-dependent lookup ). Уникальное имя является уникальным во всей программе, но в пределах единицы трансляции каждое определение безымянного пространства имён отображается на одно и то же уникальное имя: множественные определения безымянных пространств имён в одной области видимости обозначают одно и то же безымянное пространство имён.

namespace
{
    int i; // определяет ::(unique)::i
}
void f()
{
    i++;   // инкрементирует ::(unique)::i
}
namespace A
{
    namespace
    {
        int i;        // A::(unique)::i
        int j;        // A::(unique)::j
    }
    void g() { i++; } // A::(unique)::i++
}
using namespace A; // добавляет все имена из A в глобальное пространство имён
void h()
{
    i++;    // ошибка: ::(unique)::i и ::A::(unique)::i обе находятся в области видимости
    A::i++; // ok, инкрементирует ::A::(unique)::i
    j++;    // ok, инкрементирует ::A::(unique)::j
}

Несмотря на то, что имена в безымянном пространстве имён могут быть объявлены с внешней линковкой, они никогда не доступны из других единиц трансляции, поскольку имя их пространства имён уникально.

(until C++11)

Безымянные пространства имён, а также все пространства имён, объявленные прямо или косвенно внутри безымянного пространства имён, имеют внутреннюю линковку , что означает, что любое имя, объявленное внутри безымянного пространства имён, имеет внутреннюю линковку.

(since 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-объявления могут использоваться для введения членов пространства имён в другие пространства имён и области видимости блоков, или для введения членов базового класса в определения производных классов , или для введения enumerators в пространства имён, блоки и области видимости классов (since C++20) .

Объявление using с более чем одним спецификатором using эквивалентно соответствующей последовательности объявлений using с одним спецификатором using.

(since C++17)

Для использования в определениях производных классов см. using declaration .

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

void f();
namespace A
{
    void g();
}
namespace X
{
    using ::f;        // глобальная f теперь видна как ::X::f
    using A::g;       // A::g теперь видна как ::X::g
    using A::g, A::g; // (C++17) OK: двойное объявление разрешено в области пространства имён
}
void h()
{
    X::f(); // вызывает ::f
    X::g(); // вызывает A::g
}

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

namespace A
{
    void f(int);
}
using A::f; // ::f теперь является синонимом для A::f(int)
namespace A       // расширение пространства имен
{
    void f(char); // не изменяет значение ::f
}
void foo()
{
    f('a'); // вызывает f(int), несмотря на существование f(char)
}
void bar()
{
    using A::f; // эта f является синонимом для A::f(int) и A::f(char)
    f('a');     // вызывает f(char)
}

Using-объявления не могут называть template-id , или пространство имён , или scoped enumerator (до C++20) . Каждый декларатор в using-объявлении вводит одно и только одно имя, например using-объявление для enumeration не вводит ни одного из его перечислителей.

Все ограничения на обычные объявления одинаковых имен, правила сокрытия и перегрузки применяются к using-объявлениям:

namespace A
{
    int x;
}
namespace B
{
    int i;
    struct g {};
    struct x {};
    void f(int);
    void f(double);
    void g(char); // OK: имя функции g скрывает структуру g
}
void func()
{
    int i;
    using B::i;   // ошибка: i объявлена дважды
    void f(char);
    using B::f;   // OK: f(char), f(int), f(double) являются перегрузками
    f(3.5);       // вызывает B::f(double)
    using B::g;
    g('a');       // вызывает B::g(char)
    struct g g1;  // объявляет g1 с типом struct B::g
    using B::x;
    using A::x;   // OK: скрывает struct B::x
    x = 99;       // присваивает A::x
    struct x x1;  // объявляет x1 с типом 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;  // вводит B::f(int), B::f(double)
    using C::f;  // вводит C::f(int), C::f(double) и C::f(char)
    f('h');      // вызывает C::f(char)
    f(1);        // ошибка: B::f(int) или C::f(int)?
    void f(int); // ошибка: f(int) конфликтует с C::f(int) и B::f(int)
}

Если сущность объявлена, но не определена во внутреннем пространстве имён, а затем объявлена через using-объявление во внешнем пространстве имён, и затем появляется определение во внешнем пространстве имён с тем же неквалифицированным именем, это определение является членом внешнего пространства имён и конфликтует с using-объявлением:

namespace X
{
    namespace M
    {
        void g(); // объявляет, но не определяет X::M::g()
    }
    using M::g;
    void g();     // Ошибка: попытка объявить X::g, которая конфликтует с X::M::g()
}

В более общем случае, объявление, которое появляется в любой области видимости пространства имён и вводит имя с использованием неквалифицированного идентификатора, всегда вводит член в то пространство имён, в котором оно находится, а не в какое-либо другое. Исключениями являются явные инстанциации и явные специализации основного шаблона, определённого во встроенном пространстве имён: поскольку они не вводят новое имя, они могут использовать неквалифицированный идентификатор в охватывающем пространстве имён.

Директивы using

Директива using-directive является block-declaration со следующим синтаксисом:

attr  (необязательно) using namespace nested-name-specifier  (необязательно) namespace-name ; (1)
attr - (since C++11) любое количество атрибутов , применяемых к этой using-директиве
nested-name-specifier - последовательность имён и операторов разрешения области видимости :: , заканчивающаяся оператором разрешения области видимости. Одиночный :: ссылается на глобальное пространство имён. При поиске имён в этой последовательности lookup рассматривает только объявления пространств имён
namespace-name - имя пространства имён. При поиске этого имени lookup рассматривает только объявления пространств имён

Директивы using разрешены только в области видимости пространства имен scope и в блочной области видимости. С точки зрения unqualified name lookup любого имени после директивы 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;
            // Имена из A "внедряются" в D.
            // Неполный поиск в пределах D рассматривает эти имена как имеющие ту же
            // область видимости, что и глобальная область (например, для целей сокрытия имен).
            // Полный поиск с указанием D (D::name для некоторого имени)
            // найдет то же имя, что и неполный поиск в пределах D.
            int j;
            int k;
            int a = i;   // i - это B::i, потому что A::i скрыто B::i
            int b = ::i; // ошибка: в глобальном пространстве имен все еще нет i
        }
        using namespace D; // имена из D и A внедряются в C
        int k = 89; // Можно объявить имя, идентичное введенному с помощью using
        int l = k;  // неоднозначность: C::k или D::k
        int m = i;  // ok: B::i скрывает A::i
        int n = j;  // ok: D::j скрывает B::j
    }
}
// Все это эквивалентные определения:
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; // вводит D::d1, D::f, D::d2, D::f,
                   // E::e и E::f в глобальное пространство имён!
int d1;            // OK: нет конфликта с D::d1 при объявлении
namespace E
{
    int e;
    void f(int);
}
namespace D            // расширение пространства имён
{
    int d2;
    using namespace E; // транзитивная using-директива
    void f(int);
}
void f()
{
    d1++;    // ошибка: неоднозначность ::d1 или D::d1?
    ::d1++;  // OK
    D::d1++; // OK
    d2++;    // OK, d2 это D::d2
    e++;     // OK: e это E::e благодаря транзитивному using
    f(1);    // ошибка: неоднозначность: D::f(int) или E::f(int)?
    f('a');  // OK: единственная f(char) это D::f(char)
}

Примечания

Директива using using namespace std ; на любом уровне пространства имён добавляет все имена из пространства имён std в глобальное пространство имён (поскольку глобальное пространство имён является ближайшим пространством, содержащим как std , так и любое пользовательское пространство имён), что может привести к нежелательным конфликтам имён. Эта и другие using-директивы обычно считаются плохой практикой на уровне файла в заголовочных файлах ( SF.7: Не используйте using namespace на глобальном уровне в заголовочных файлах ).

Макрос тестирования возможностей Значение Стандарт Возможность
__cpp_namespace_attributes 201411L (C++17) Атрибуты для пространств имён

Ключевые слова

namespace , using , inline

Пример

Этот пример показывает, как использовать пространство имён для создания класса, который уже был назван в 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 не было
применено к объявлениям классов и перечислений
применено

Смотрите также

namespace alias создает псевдоним существующего пространства имен