Suche…


Einführung

Ein Namensraum ist ein deklaratives Präfix für Funktionen, Klassen, Typen usw., um Namenskollisionen bei der Verwendung mehrerer Bibliotheken zu verhindern.

Syntax

  • Namespace - Kennung (opt) {declaration-Seq}
  • Inline-Namespace- ID ( opt ) { Deklaration-seq } / * seit C ++ 11 * /
  • Inline ( opt ) -Namensraum Attribut- Bezeichner -Seq- Bezeichner ( Opt ) { Deklaration-Seq } / * seit C ++ 17 * /
  • Namespace einschließender-Namespace- Bezeichner : ID { Deklaration-Seq } / * seit C ++ 17 * /
  • Namespace- ID = Qualifizierter-Namespace- Bezeichner ;
  • unter Verwendung des Namespaces verschachtelter-Name-Bezeichner ( opt ) Namespace-Name ;
  • Attributspezifizierer-seq unter Verwendung des Namespaces verschachtelter Namensspezifizierer ( opt ) Namespace-Name ; / * seit C ++ 11 * /

Bemerkungen

Das Schlüsselwort namespace hat drei verschiedene Bedeutungen je nach Kontext:

  1. Wenn auf einen optionalen Namen und eine geschweifte Klammerfolge von Deklarationen gefolgt wird, wird ein neuer Namespace definiert oder ein vorhandener Namespace um diese Deklarationen erweitert. Wenn der Name weggelassen wird, ist der Namespace ein unbenannter Namespace .

  2. Wenn ein Name und ein Gleichheitszeichen gefolgt werden, wird ein Namespace-Alias ​​angegeben .

  3. Wenn vorangestelltem using und von einem Namespace - Namen gefolgt, bildet es ein using - Direktive , die Namen im angegebenen Namespace ermöglicht durch unqualifizierte Namen - Suche gefunden werden (aber nicht die Namen im aktuellen Bereich neu deklarieren). Eine using-Direktive darf nicht im Klassenbereich vorkommen.

using namespace std; ist entmutigt. Warum? Denn der namespace std ist riesig! Dies bedeutet, dass die Wahrscheinlichkeit groß ist, dass Namen kollidieren:

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

Was sind Namespaces?

Ein C ++ - Namespace ist eine Sammlung von C ++ - Entitäten (Funktionen, Klassen, Variablen), deren Namen den Namen des Namespaces voranstellen. Beim Schreiben von Code innerhalb eines Namespaces müssen benannten Entitäten, die zu diesem Namespace gehören, nicht der Name des Namespaces vorangestellt werden. Entitäten außerhalb dieses Namespaces müssen jedoch den vollständig qualifizierten Namen verwenden. Der vollständig qualifizierte Name hat das Format <namespace>::<entity> . Beispiel:

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.

Namespaces sind nützlich, um zusammengehörige Definitionen zu gruppieren. Nehmen Sie die Analogie eines Einkaufszentrums. In der Regel ist ein Einkaufszentrum in mehrere Geschäfte aufgeteilt, von denen jeder Artikel aus einer bestimmten Kategorie verkauft. Ein Geschäft kann Elektronik verkaufen, während ein anderes Geschäft Schuhe verkaufen kann. Diese logischen Trennungen in den Geschäftstypen helfen den Käufern, die Artikel zu finden, nach denen sie suchen. Namespaces helfen C ++ - Programmierern wie Käufern dabei, die gewünschten Funktionen, Klassen und Variablen zu finden, indem sie auf logische Weise organisiert werden. Beispiel:

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

Es ist ein einzelner Namespace vordefiniert. Hierbei handelt es sich um den globalen Namespace, der keinen Namen hat, aber mit :: bezeichnet werden kann. Beispiel:

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

Namensräume erstellen

Das Erstellen eines Namespaces ist sehr einfach:

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

So rufen Sie bar , müssen Sie zuerst den Namespace angeben, gefolgt von dem Bereichsauflösungsoperator :: :

Foo::bar();

Es ist erlaubt, einen Namespace in einem anderen zu erstellen, zum Beispiel:

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

Der obige Code könnte folgendermaßen vereinfacht werden:

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

Namensräume erweitern

Eine nützliche Funktion von namespace ist, dass Sie sie erweitern (Mitglieder hinzufügen) können.

namespace Foo
{
    void bar() {}
}

//some other stuff

namespace Foo
{
    void bar2() {}
}

Direktive verwenden

Das Schlüsselwort "using" hat drei Varianten. Zusammen mit dem Schlüsselwort 'Namespace' schreiben Sie eine 'using-Direktive':

Wenn Sie nicht Foo:: vor jedem Zeugs im Namespace Foo schreiben möchten, können Sie den using namespace Foo; alles aus Foo zu importieren.

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

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

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

Es ist auch möglich, ausgewählte Entitäten in einem Namensraum und nicht im gesamten Namensraum zu importieren:

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

Ein Wort der Vorsicht: Die using namespace in Header-Dateien wird in den meisten Fällen als fehlerhaft angesehen. In diesem Fall wird der Namespace in jede Datei importiert, die den Header enthält. Da gibt es keine Möglichkeit der „un- using “ einem Namespace, dies zu Namespace Verschmutzung führen kann (mehr oder unerwarteter Symbole im globalen Namespace) oder, schlimmer noch, Konflikte. In diesem Beispiel wird das Problem veranschaulicht:

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

Eine using-Direktive darf nicht im Klassenbereich vorkommen.

Argumentabhängige Suche

Beim Aufrufen einer Funktion ohne expliziten Namespace-Qualifikationsmerkmal kann der Compiler eine Funktion innerhalb eines Namespaces aufrufen, wenn sich einer der Parametertypen für diese Funktion ebenfalls in diesem Namespace befindet. Dies wird als "Argumentabhängige Suche" oder ADL bezeichnet:

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 schlägt fehl, da keiner seiner Parametertypen aus dem Test Namespace stammt. call_too funktioniert, weil SomeClass Mitglied von Test und sich daher für ADL-Regeln qualifiziert.

Wann tritt ADL nicht auf

ADL tritt nicht auf, wenn die normale unqualifizierte Suche nach einem Klassenmitglied, einer im Blockbereich deklarierten Funktion oder einem nicht funktionellen Typ sucht. Zum Beispiel:

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

Inline-Namespace

C ++ 11

inline namespace enthält den Inhalt des Inline-Namespaces im umschließenden Namespace, so

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

ist meistens äquivalent zu

namespace Outer
{

    namespace Inner
    {
        void foo();
    }

    using Inner::foo;
}

Elemente aus Outer::Inner:: und denen, die Outer:: sind, sind jedoch identisch.

Folgendes ist also gleichwertig

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

Die Alternative using namespace Inner; wäre für einige knifflige Teile als Schablonenspezialisierung nicht gleichwertig:

Zum

#include <outer.h> // See below

class MyCustomType;
namespace Outer
{
    template <>
    void foo<MyCustomType>() { std::cout << "Specialization"; }
}
  • Der Inline-Namespace ermöglicht die Spezialisierung von Outer::foo

    // outer.h
    // include guard omitted for simplification
    
    namespace Outer
    {
        inline namespace Inner
        {
            template <typename T>
            void foo() { std::cout << "Generic"; }
        }
    }
    
  • Während der using namespace die Spezialisierung von Outer::foo nicht zulässt

    // 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 ist eine Möglichkeit, mehreren Versionen das Zusammenleben und die Verwendung der inline Version zu ermöglichen

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

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

}

Und mit der Nutzung

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

Unbenannte / anonyme Namespaces

Ein unbenannter Namespace kann verwendet werden, um sicherzustellen, dass Namen über eine interne Verknüpfung verfügen (auf die nur die aktuelle Übersetzungseinheit verweist). Ein solcher Namespace wird wie jeder andere Namespace definiert, jedoch ohne den Namen:

namespace {
    int foo = 42;
}

foo ist nur in der Übersetzungseinheit sichtbar, in der es angezeigt wird.

Es wird empfohlen, niemals unbenannte Namespaces in Header-Dateien zu verwenden, da hierdurch für jede Übersetzungseinheit, in der sie enthalten ist, eine Version des Inhalts angezeigt wird. Dies ist besonders wichtig, wenn Sie Nicht-Konstante-Globals definieren.

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

Kompakte verschachtelte Namespaces

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

Sie können sowohl die Eingabe a und b Namespaces in einem Schritt mit namespace a::b beginnend in C ++ 17.

Aliasing eines langen Namespaces

Dies wird normalerweise zum Umbenennen oder Verkürzen langer Namespace-Referenzen verwendet, z.

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

Alias Erklärung werden durch Verwendung der vorstehenden Aussagen betroffen

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


using namespace boost;

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

Es ist jedoch einfacher zu verwechseln, welchen Namespace Sie als Aliasing verwenden, wenn Sie Folgendes haben:

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

Einem Namespace kann ein Alias ​​( dh ein anderer Name für denselben Namespace) mithilfe der namespace ID bezeichner = zugewiesen werden. Auf Mitglieder des Alias-Namespaces kann zugegriffen werden, indem sie mit dem Namen des Alias ​​qualifiziert werden. Im folgenden Beispiel ist der geschachtelte Namespace AReallyLongName::AnotherReallyLongName ungünstig, sodass die Funktion qux lokal einen Alias N deklariert. Auf Mitglieder dieses Namensraums kann dann einfach mit N:: zugegriffen werden.

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
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow