Поиск…


Вступление

Используется для предотвращения конфликтов имен при использовании нескольких библиотек, пространство имен является декларативным префиксом для функций, классов, типов и т. Д.

Синтаксис

  • Идентификатор пространства имен ( 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 ключевых слов имеет три разных значения в зависимости от контекста:

  1. Когда за ним следует необязательное имя и последовательность объявлений, заключенная в фигурной скобке, он определяет новое пространство имен или расширяет существующее пространство имен с этими объявлениями. Если имя опущено, пространство имен является безымянным пространством имен .

  2. Когда за ним следует имя и знак равенства, он объявляет псевдоним пространства имен .

  3. Когда ему предшествует 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() {}
        }
    }
}
C ++ 17

Вышеприведенный код можно упростить до следующего:

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"
}

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

C ++ 11

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

Компактные вложенные пространства имен

C ++ 17
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());
}


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow