Zoeken…


Invoering

Gebruikt om naambotsingen te voorkomen bij gebruik van meerdere bibliotheken, is een naamruimte een declaratief voorvoegsel voor functies, klassen, typen, enz.

Syntaxis

  • namespace identifier ( opt ) { laration-seq }
  • inline namespace identifier ( opt ) { laration-seq } / * sinds C ++ 11 * /
  • inline ( opt ) namespace attribuut-specifier-seq identifier ( opt ) { laration-seq } / * sinds C ++ 17 * /
  • namespace enclosing-namespace-specifier :: identifier { laration-seq } / * sinds C ++ 17 * /
  • namespace identifier = gekwalificeerde-naamruimte-specificatie ;
  • gebruik van naamruimte genest-naam-specificeerder ( opt ) naamruimte-naam ;
  • attribuut-specificeer-seq gebruikmakend van naamruimte genest-naam-specificeer ( opt ) naamruimte-naam ; / * sinds C ++ 11 * /

Opmerkingen

De namespace zoekwoord heeft drie verschillende betekenissen, afhankelijk van de context:

  1. Wanneer het wordt gevolgd door een optionele naam en een reeks accolades tussen accolades, wordt een nieuwe naamruimte gedefinieerd of wordt een bestaande naamruimte met die verklaringen uitgebreid. Als de naam wordt weggelaten, is de naamruimte een naamloze naamruimte .

  2. Wanneer het wordt gevolgd door een naam en een gelijkteken, wordt een alias voor de naamruimte verklaard.

  3. Wanneer het wordt voorafgegaan door het using en gevolgd door een naamruimtenaam, vormt het een gebruiksrichtlijn , waarmee namen in de opgegeven naamruimte kunnen worden gevonden door ongekwalificeerde naamopzoeking (maar geeft deze namen niet opnieuw op in het huidige bereik). Een gebruiksrichtlijn kan niet voorkomen in de klasse scope.

using namespace std; wordt afgeraden. Waarom? Omdat namespace std enorm is! Dit betekent dat er een grote kans is dat namen met elkaar botsen:

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

Wat zijn naamruimten?

Een C ++ -naamruimte is een verzameling C ++ -entiteiten (functies, klassen, variabelen), waarvan de namen worden voorafgegaan door de naam van de naamruimte. Bij het schrijven van code in een naamruimte hoeven benoemde entiteiten die bij die naamruimte horen niet voorafgegaan te worden door de naamruimtenaam, maar entiteiten daarbuiten moeten de volledig gekwalificeerde naam gebruiken. De volledig gekwalificeerde naam heeft het formaat <namespace>::<entity> . Voorbeeld:

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.

Naamruimten zijn handig voor het groeperen van gerelateerde definities. Neem de analogie van een winkelcentrum. Over het algemeen is een winkelcentrum opgedeeld in verschillende winkels, waarbij elke winkel items uit een specifieke categorie verkoopt. De ene winkel verkoopt misschien elektronica, terwijl een andere winkel schoenen verkoopt. Deze logische scheidingen in winkeltypen helpen de shoppers de items te vinden waarnaar ze op zoek zijn. Namespaces helpen c ++ programmeurs, zoals shoppers, om de functies, klassen en variabelen te vinden waarnaar ze op zoek zijn door ze op een logische manier te organiseren. Voorbeeld:

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

Er is één vooraf gedefinieerde naamruimte. Dit is de algemene naamruimte die geen naam heeft, maar kan worden aangeduid met :: . Voorbeeld:

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

Naamruimten maken

Een naamruimte maken is heel eenvoudig:

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

Om de bar te roepen, moet u eerst de naamruimte opgeven, gevolgd door de operator voor :: ::

Foo::bar();

Het is toegestaan om de ene naamruimte in de andere te maken, bijvoorbeeld:

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

De bovenstaande code kan worden vereenvoudigd tot het volgende:

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

Naamruimten uitbreiden

Een handige functie van namespace is dat u ze kunt uitbreiden (leden eraan toevoegen).

namespace Foo
{
    void bar() {}
}

//some other stuff

namespace Foo
{
    void bar2() {}
}

Richtlijn gebruiken

Het trefwoord 'gebruiken' heeft drie smaken. Gecombineerd met trefwoord 'namespace' schrijf je een 'usingrichtlijn':

Als je geen Foo:: voor alle dingen in de naamruimte Foo wilt schrijven, kun je de using namespace Foo; om alles uit Foo te importeren.

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

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

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

Het is ook mogelijk om geselecteerde entiteiten in een naamruimte te importeren in plaats van de hele naamruimte:

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

Een woord van waarschuwing: het using namespace in header-bestanden wordt in de meeste gevallen als een slechte stijl gezien. Als dit wordt gedaan, wordt de naamruimte geïmporteerd in elk bestand met de koptekst. Aangezien er geen manier is om een naamruimte niet meer te using , kan dit leiden tot vervuiling van de naamruimte (meer of onverwachte symbolen in de globale naamruimte) of, erger nog, conflicten. Zie dit voorbeeld voor een illustratie van het probleem:

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

Een gebruiksrichtlijn kan niet voorkomen in de klasse scope.

Argumentafhankelijke zoekopdracht

Bij het aanroepen van een functie zonder een expliciete naamruimte-kwalificatie, kan de compiler ervoor kiezen om een functie in een naamruimte aan te roepen als een van de parametertypen voor die functie zich ook in die naamruimte bevindt. Dit wordt "Argumentafhankelijke zoekopdracht" of ADL genoemd:

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 mislukt omdat geen van de parametertypen uit de naamruimte Test . call_too werkt omdat SomeClass lid is van Test en daarom in aanmerking komt voor ADL-regels.

Wanneer komt ADL niet voor

ADL treedt niet op als normaal niet-gekwalificeerd opzoeken een lid van de klasse, een functie die is gedeclareerd bij blokbereik of iets dat niet van het functietype is, vindt. Bijvoorbeeld:

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 naamruimte

C ++ 11

inline namespace bevat de inhoud van de inline namespace in de omringende naamruimte, dus

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

is meestal gelijk aan

namespace Outer
{

    namespace Inner
    {
        void foo();
    }

    using Inner::foo;
}

maar element uit Outer::Inner:: en die geassocieerd zijn met Outer:: zijn identiek.

Dus volgen is equivalent

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

Het alternatief using namespace Inner; zou voor sommige lastige onderdelen niet equivalent zijn als sjabloon-specialisatie:

Voor

#include <outer.h> // See below

class MyCustomType;
namespace Outer
{
    template <>
    void foo<MyCustomType>() { std::cout << "Specialization"; }
}
  • De inline naamruimte maakt de specialisatie van Outer::foo

    // outer.h
    // include guard omitted for simplification
    
    namespace Outer
    {
        inline namespace Inner
        {
            template <typename T>
            void foo() { std::cout << "Generic"; }
        }
    }
    
  • Terwijl de using namespace niet de specialisatie Outer::foo toestaat

    // 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-naamruimte is een manier om meerdere versies samen te laten wonen en standaard in te stellen op de inline

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

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

}

En met gebruik

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

Naamloze / anonieme naamruimten

Een naamloze naamruimte kan worden gebruikt om ervoor te zorgen dat namen een interne koppeling hebben (waarnaar alleen door de huidige vertaaleenheid kan worden verwezen). Een dergelijke naamruimte wordt op dezelfde manier gedefinieerd als elke andere naamruimte, maar zonder de naam:

namespace {
    int foo = 42;
}

foo is alleen zichtbaar in de vertaaleenheid waarin het wordt weergegeven.

Het wordt aanbevolen om nooit naamloze naamruimten in header-bestanden te gebruiken, omdat dit een versie van de inhoud geeft voor elke vertaaleenheid waarin het is opgenomen. Dit is vooral belangrijk als u niet-const-globalen definieert.

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

Compacte geneste naamruimten

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

U kunt zowel op de enter a en b naamruimten in één stap met namespace a::b beginnen in C ++ 17.

Aliasing van een lange naamruimte

Dit wordt meestal gebruikt voor het hernoemen of verkorten van lange naamruimte-verwijzingen, zoals verwijzingen naar componenten van een bibliotheek.

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 Declaration scope

Aliasverklaring wordt beïnvloed door voorafgaande gebruik van verklaringen

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


using namespace boost;

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

Het is echter gemakkelijker om in de war te raken over de naamruimte die u gebruikt als u zoiets hebt:

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;

Naamruimte alias

Een naamruimte kan een alias krijgen (dat wil zeggen een andere naam voor dezelfde naamruimte) met behulp van de namespace identifier = syntaxis. Leden van de aliasnaamruimte kunnen worden geopend door ze te kwalificeren met de naam van de alias. In het volgende voorbeeld is de geneste naamruimte AReallyLongName::AnotherReallyLongName onhandig om te typen, dus qux de functie qux lokaal een alias N Leden van die naamruimte kunnen dan eenvoudig worden geopend met behulp van 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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow