Ricerca…


introduzione

Utilizzato per prevenire conflitti di nomi quando si utilizzano più librerie, uno spazio dei nomi è un prefisso dichiarativo per funzioni, classi, tipi, ecc.

Sintassi

  • identificatore di spazio dei nomi ( opt ) { declaration-seq }
  • identificatore di spazio dei nomi in linea ( opt ) { declaration-seq } / * dal C ++ 11 * /
  • inline ( opt ) namespace attributo- identificatore -seq identificatore ( opt ) { declaration-seq } / * dal C ++ 17 * /
  • namespace enclosing-namespace-specificifier :: identificatore { declaration-seq } / * dal C ++ 17 * /
  • identificatore dello spazio dei nomi = specificatore del namespace qualificato ;
  • usando namespace nested-name-specifier ( opt ) nome-spazio-nome ;
  • attribute-specificatore-seq utilizzando namespace nested-name-specificatore ( opt ) nome-spazio-nome ; / * dal C ++ 11 * /

Osservazioni

Lo namespace parole chiave ha tre significati diversi a seconda del contesto:

  1. Se seguito da un nome facoltativo e da una sequenza di dichiarazioni racchiuse tra parentesi, definisce un nuovo spazio dei nomi o estende uno spazio dei nomi esistente con tali dichiarazioni. Se il nome è omesso, lo spazio dei nomi è uno spazio dei nomi senza nome .

  2. Quando seguito da un nome e un segno di uguale, dichiara un alias di namespace .

  3. Quando è preceduto using e seguito da un nome di spazio dei nomi, forma una direttiva using , che consente di trovare nomi nello spazio dei nomi dato da una ricerca di nome non qualificata (ma non ridichiara quei nomi nell'ambito corrente). Una direttiva using non può verificarsi nell'ambito della classe.

using namespace std; è scoraggiato Perché? Perché lo namespace std è enorme! Ciò significa che c'è un'alta probabilità che i nomi entrino in collisione:

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

Cosa sono gli spazi dei nomi?

Uno spazio dei nomi C ++ è una raccolta di entità C ++ (funzioni, classi, variabili), i cui nomi sono preceduti dal nome del namespace. Quando si scrive codice all'interno di uno spazio dei nomi, le entità denominate appartenenti a tale spazio dei nomi non devono essere precedute dal nome del namespace, ma le entità esterne devono utilizzare il nome completo. Il nome completo ha il formato <namespace>::<entity> . Esempio:

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.

I namespace sono utili per raggruppare insieme le definizioni correlate. Prendi l'analogia di un centro commerciale. Generalmente un centro commerciale è suddiviso in diversi negozi, ogni negozio vende articoli da una specifica categoria. Un negozio potrebbe vendere prodotti elettronici, mentre un altro negozio potrebbe vendere scarpe. Queste separazioni logiche nei tipi di negozi aiutano gli acquirenti a trovare gli articoli che stanno cercando. I namespace aiutano i programmatori c ++, come gli acquirenti, a trovare le funzioni, le classi e le variabili che stanno cercando organizzandole in modo logico. Esempio:

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

C'è un singolo spazio dei nomi predefinito, che è lo spazio dei nomi globale che non ha nome, ma può essere indicato da :: . Esempio:

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

Creare spazi dei nomi

Creare un namespace è davvero semplice:

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

Per chiamare la bar , devi prima specificare lo spazio dei nomi, seguito dall'operatore di risoluzione dell'ambito ::

Foo::bar();

È consentito creare uno spazio dei nomi in un altro, ad esempio:

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

Il codice sopra riportato potrebbe essere semplificato come segue:

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

Estendere spazi dei nomi

Un'utile caratteristica dello namespace dei namespace è che è possibile espanderli (aggiungere membri ad esso).

namespace Foo
{
    void bar() {}
}

//some other stuff

namespace Foo
{
    void bar2() {}
}

Usando la direttiva

La parola chiave 'using' ha tre sapori. Combinato con la parola chiave 'namespace' scrivi una 'direttiva using':

Se non vuoi scrivere Foo:: davanti a tutte le cose nel namespace Foo , puoi usare using namespace Foo; per importare ogni singola cosa da Foo .

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

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

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

È anche possibile importare entità selezionate in uno spazio dei nomi anziché nell'intero spazio dei nomi:

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

Una parola di cautela: l' using namespace nei file di intestazione è visto come uno stile sbagliato nella maggior parte dei casi. Se ciò è fatto, lo spazio dei nomi viene importato in ogni file che include l'intestazione. Poiché non c'è modo di "non using " uno spazio dei nomi, questo può portare all'inquinamento dello spazio dei nomi (simboli più o inaspettati nello spazio dei nomi globale) o, peggio, ai conflitti. Vedi questo esempio per un'illustrazione del problema:

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

Una direttiva using non può verificarsi nell'ambito della classe.

Ricerca dipendente dall'argomento

Quando si chiama una funzione senza un qualificatore di namespace esplicito, il compilatore può scegliere di chiamare una funzione all'interno di uno spazio dei nomi se uno dei tipi di parametro di quella funzione è anche in quello spazio dei nomi. Questo è chiamato "Argument Dipendent Lookup", o 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 fallisce perché nessuno dei suoi tipi di parametro proviene dallo spazio dei nomi Test . call_too funziona perché SomeClass è un membro di Test e pertanto si qualifica per le regole ADL.

Quando non si verifica ADL

ADL non si verifica se la normale ricerca non qualificata trova un membro della classe, una funzione che è stata dichiarata nell'ambito di un blocco o qualcosa che non è di tipo funzione. Per esempio:

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

Spazio dei nomi in linea

C ++ 11

inline namespace include il contenuto dello inline namespace include, quindi

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

è per lo più equivalente a

namespace Outer
{

    namespace Inner
    {
        void foo();
    }

    using Inner::foo;
}

ma l'elemento di Outer::Inner:: e quelli associati a Outer:: sono identici.

Quindi seguire è equivalente

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

L'alternativa using namespace Inner; non sarebbe equivalente per alcune parti difficili come la specializzazione template:

Per

#include <outer.h> // See below

class MyCustomType;
namespace Outer
{
    template <>
    void foo<MyCustomType>() { std::cout << "Specialization"; }
}
  • Lo spazio dei nomi in linea consente la specializzazione di Outer::foo

    // outer.h
    // include guard omitted for simplification
    
    namespace Outer
    {
        inline namespace Inner
        {
            template <typename T>
            void foo() { std::cout << "Generic"; }
        }
    }
    
  • Mentre l' using namespace non consente la specializzazione di 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`.
    }
    

Lo spazio dei nomi in linea è un modo per consentire a più versioni di convivere e l'impostazione predefinita a quella in inline

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

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

}

E con l'uso

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

Spazi dei nomi senza nome / anonimi

È possibile utilizzare uno spazio dei nomi senza nome per garantire che i nomi abbiano un collegamento interno (è possibile fare riferimento solo all'unità di traduzione corrente). Tale spazio dei nomi è definito allo stesso modo di qualsiasi altro spazio dei nomi, ma senza il nome:

namespace {
    int foo = 42;
}

foo è visibile solo nell'unità di traduzione in cui appare.

Si consiglia di non utilizzare mai spazi dei nomi non denominati nei file di intestazione in quanto fornisce una versione del contenuto per ogni unità di traduzione in cui è inclusa. Ciò è particolarmente importante se si definiscono globali non costanti.

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

Spazi dei nomi nidificati compatti

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

È possibile inserire sia l' a e b spazi dei nomi in un unico passaggio con namespace a::b a partire dal C ++ 17.

Aliasing di un lungo spazio dei nomi

Questo di solito è usato per rinominare o accorciare lunghi riferimenti di namespace come riferimenti a componenti di una libreria.

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

Ambito di dichiarazione alias

La Dichiarazione alias è influenzata dalle precedenti dichiarazioni usando

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


using namespace boost;

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

Tuttavia, è più facile confondersi su quale spazio dei nomi stai facendo aliasing quando hai qualcosa del genere:

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 ​​dello spazio dei nomi

Ad uno spazio dei nomi può essere assegnato un alias ( ovvero un altro nome per lo stesso spazio dei nomi) utilizzando la sintassi dello namespace dei namespace identificatore = . È possibile accedere ai membri dello spazio dei nomi con alias qualificandoli con il nome dell'alias. Nell'esempio seguente, lo spazio AReallyLongName::AnotherReallyLongName nomi nidificato AReallyLongName::AnotherReallyLongName è scomodo da digitare, quindi la funzione qux locale dichiara un alias N È possibile accedere ai membri di tale spazio dei nomi semplicemente utilizzando 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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow