Szukaj…


Wprowadzenie

Stosowany w celu zapobiegania kolizjom nazw podczas korzystania z wielu bibliotek, przestrzeń nazw jest deklaratywnym przedrostkiem funkcji, klas, typów itp.

Składnia

  • identyfikator przestrzeni nazw ( opt ) { deklaracja-seq }
  • wbudowany identyfikator przestrzeni nazw ( opt ) { deklaracja-seq } / * od C ++ 11 * /
  • inline ( opt ) przestrzeń nazw atrybut-specyfikator- identyfikator -sekw. ( opt ) { deklaracja-sekw. } / * od C ++ 17 * /
  • przestrzeń nazw otaczająca-nazwa-specyfikator :: identyfikator { deklaracja-sekw. } / * od C ++ 17 * /
  • identyfikator przestrzeni nazw = kwalifikowany-nazwa-specyfikator ;
  • using namespace nested-name-specifier ( opt ) namespace-name ;
  • atrybut-specifier-seq przy użyciu przestrzeni nazw nested-name-specifier ( opt ) namespace-name ; / * od C ++ 11 * /

Uwagi

Przestrzeń namespace słów kluczowych ma trzy różne znaczenia w zależności od kontekstu:

  1. Po której następuje opcjonalna nazwa i sekwencja deklaracji w nawiasach klamrowych, definiuje ona nową przestrzeń nazw lub rozszerza istniejącą przestrzeń nazw o te deklaracje. Jeśli nazwa zostanie pominięta, przestrzeń nazw jest przestrzenią nazw bez nazwy .

  2. Po nazwie i znaku równości deklaruje alias przestrzeni nazw .

  3. Kiedy poprzedzone using a następnie nazwa przestrzeni nazw, tworzy za pomocą dyrektywy , która pozwala nazw w danej przestrzeni nazw można znaleźć według nazwy niewykwalifikowanego odnośnika (ale nie redeclare te nazwy w bieżącym zakresie). Dyrektywa użycia nie może wystąpić w zakresie klasy.

using namespace std; jest odradzany. Dlaczego? Ponieważ namespace std jest ogromna! Oznacza to, że istnieje duże prawdopodobieństwo kolizji nazw:

//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

Jakie są przestrzenie nazw?

Przestrzeń nazw C ++ to zbiór encji C ++ (funkcje, klasy, zmienne), których nazwy są poprzedzone nazwą przestrzeni nazw. Podczas pisania kodu w przestrzeni nazw nazwane podmioty należące do tej przestrzeni nazw nie muszą być poprzedzone nazwą przestrzeni nazw, ale jednostki spoza niej muszą używać w pełni kwalifikowanej nazwy. Pełna nazwa ma format <namespace>::<entity> nazwa <namespace>::<entity> . Przykład:

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.

Przestrzenie nazw są przydatne do grupowania powiązanych definicji. Weź analogię do centrum handlowego. Zasadniczo centrum handlowe jest podzielone na kilka sklepów, z których każdy sprzedaje przedmioty z określonej kategorii. Jeden sklep może sprzedawać elektronikę, podczas gdy inny sklep może sprzedawać buty. Te logiczne separacje w typach sklepów pomagają kupującym znaleźć poszukiwane produkty. Przestrzenie nazw pomagają programistom c ++, takim jak kupujący, znaleźć funkcje, klasy i zmienne, których szukają, organizując je w logiczny sposób. Przykład:

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.)
    };
}

Istnieje predefiniowana pojedyncza przestrzeń nazw, która jest globalną przestrzenią nazw, która nie ma nazwy, ale może być oznaczona przez :: . Przykład:

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

Tworzenie przestrzeni nazw

Utworzenie przestrzeni nazw jest naprawdę łatwe:

//Creates namespace foo
namespace Foo
{
    //Declares function bar in namespace foo
    void bar() {}
}

Zadzwonić bar , trzeba najpierw określić przestrzeń nazw, a następnie przez operatora rozdzielczość zakres :: :

Foo::bar();

Dozwolone jest tworzenie jednej przestrzeni nazw w innej, na przykład:

namespace A
{
    namespace B
    {
        namespace C
        {
            void bar() {}
        }
    }
}
C ++ 17

Powyższy kod można uprościć w następujący sposób:

namespace A::B::C
{
    void bar() {}
}

Rozszerzanie przestrzeni nazw

Przydatną funkcją namespace jest to, że możesz je rozszerzać (dodawać do niego członków).

namespace Foo
{
    void bar() {}
}

//some other stuff

namespace Foo
{
    void bar2() {}
}

Korzystanie z dyrektywy

Słowo kluczowe „używanie” ma trzy smaki. W połączeniu ze słowem kluczowym „przestrzeń nazw” piszesz „za pomocą dyrektywy”:

Jeśli nie chcesz pisać Foo:: na początku każdego elementu w przestrzeni nazw Foo , możesz użyć using namespace Foo; aby zaimportować każdą rzecz z Foo .

namespace Foo
{
    void bar() {}
    void baz() {}
}

//Have to use Foo::bar()
Foo::bar();

//Import Foo
using namespace Foo;
bar(); //OK
baz(); //OK

Możliwe jest również importowanie wybranych encji w przestrzeni nazw zamiast w całej przestrzeni nazw:

using Foo::bar;
bar(); //OK, was specifically imported
baz(); // Not OK, was not imported

Uwaga: using namespace w plikach nagłówkowych jest w większości przypadków postrzegane jako zły styl. Jeśli tak się stanie, przestrzeń nazw zostanie zaimportowana do każdego pliku zawierającego nagłówek. Ponieważ nie ma sposobu na „nie- using ” przestrzeń nazw, może to prowadzić do zanieczyszczenia przestrzeni nazw (lub bardziej nieoczekiwane symbole w globalnej przestrzeni nazw) lub, co gorsza, konfliktów. Zobacz ten przykład, aby zilustrować problem:

/***** 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

Dyrektywa użycia nie może wystąpić w zakresie klasy.

Wyszukiwanie zależne od argumentu

Podczas wywoływania funkcji bez jawnego kwalifikatora przestrzeni nazw kompilator może wybrać wywołanie funkcji w przestrzeni nazw, jeśli jeden z typów parametrów tej funkcji również znajduje się w tej przestrzeni nazw. Nazywa się to „Argument Dependent Lookup” lub 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 kończy się niepowodzeniem, ponieważ żaden z jego typów parametrów nie pochodzi z przestrzeni nazw Test . call_too działa, ponieważ SomeClass jest członkiem Test i dlatego kwalifikuje się do reguł ADL.

Kiedy ADL nie występuje

ADL nie występuje, jeśli normalne niewykwalifikowane wyszukiwanie znajdzie element klasy, funkcję zadeklarowaną w zakresie bloku lub coś, co nie jest typu funkcji. Na przykład:

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

Wbudowana przestrzeń nazw

C ++ 11

inline namespace zawiera zawartość inline namespace nazw w otaczającej przestrzeni nazw, więc

namespace Outer
{
    inline namespace Inner
    {
        void foo();
    }
}

jest w większości równoważny z

namespace Outer
{

    namespace Inner
    {
        void foo();
    }

    using Inner::foo;
}

ale elementy z Outer::Inner:: i te powiązane z Outer:: są identyczne.

Więc podążanie jest równoważne

Outer::foo();
Outer::Inner::foo();

Alternatywa using namespace Inner; nie byłby równoważny dla niektórych trudnych części jako specjalizacja szablonu:

Dla

#include <outer.h> // See below

class MyCustomType;
namespace Outer
{
    template <>
    void foo<MyCustomType>() { std::cout << "Specialization"; }
}
  • Wbudowana przestrzeń nazw pozwala na specjalizację Outer::foo

    // outer.h
    // include guard omitted for simplification
    
    namespace Outer
    {
        inline namespace Inner
        {
            template <typename T>
            void foo() { std::cout << "Generic"; }
        }
    }
    
  • Natomiast using namespace nie pozwala na specjalizację 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`.
    }
    

Wbudowana przestrzeń nazw to sposób na umożliwienie współistnienia kilku wersji i domyślnego ustawienia tej inline

namespace MyNamespace
{
    // Inline the last version
    inline namespace Version2
    {
        void foo(); // New version
        void bar();
    }

    namespace Version1 // The old one
    {
        void foo();
    }

}

I przy użyciu

MyNamespace::Version1::foo(); // old version
MyNamespace::Version2::foo(); // new version
MyNamespace::foo();           // default version : MyNamespace::Version1::foo();

Nienazwane / anonimowe przestrzenie nazw

Można użyć nienazwanej przestrzeni nazw, aby upewnić się, że nazwy mają wewnętrzne powiązanie (do której może odwoływać się tylko bieżąca jednostka tłumacząca). Taka przestrzeń nazw jest zdefiniowana w taki sam sposób, jak każda inna przestrzeń nazw, ale bez nazwy:

namespace {
    int foo = 42;
}

foo jest widoczne tylko w jednostce tłumaczenia, w której się pojawia.

Zaleca się, aby nigdy nie używać nienazwanych przestrzeni nazw w plikach nagłówków, ponieważ daje to wersję treści dla każdej jednostki tłumaczeniowej, w której jest zawarty. Jest to szczególnie ważne, jeśli definiujesz globale nie stałe.

// 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

Kompaktowe zagnieżdżone przestrzenie nazw

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 {};
}

Można wprowadzić zarówno a i b nazw w jednym kroku z namespace a::b począwszy od C ++ 17.

Aliasing długiej przestrzeni nazw

Jest to zwykle używane do zmiany nazwy lub skrócenia długich odwołań do przestrzeni nazw, takich jak odwołania do składników biblioteki.

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

Alias Zakres deklaracji

Na deklarację aliasu ma wpływ wcześniejsze użycie instrukcji

namespace boost
{
    namespace multiprecision
    {
        class Number ...
    }
}


using namespace boost;

//   Both Namespace are equivalent 
namespace Name1 = boost::multiprecision;
namespace Name2 = multiprecision;

Łatwiej jednak pomylić, która przestrzeń nazw jest aliasing, gdy masz coś takiego:

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;

Alias przestrzeni nazw

Przestrzeni nazw można nadać alias ( tj. Inną nazwę dla tej samej przestrzeni nazw) za pomocą identyfikatora namespace = składnia. Dostęp do członków aliasu przestrzeni nazw można uzyskać, kwalifikując ich przy użyciu nazwy aliasu. W poniższym przykładzie zagnieżdżona przestrzeń nazw AReallyLongName::AnotherReallyLongName jest niewygodna do AReallyLongName::AnotherReallyLongName , więc funkcja qux lokalnie deklaruje alias N Dostęp do członków tej przestrzeni nazw można uzyskać po prostu za pomocą 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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow