Recherche…


Introduction

Utilisé pour empêcher les collisions de noms lors de l'utilisation de plusieurs bibliothèques, un espace de noms est un préfixe déclaratif pour les fonctions, les classes, les types, etc.

Syntaxe

  • identifiant d' espace de noms ( opt ) { declaration-seq }
  • identificateur d' espace de nommage en ligne ( opt ) { declaration-seq } / * depuis C ++ 11 * /
  • inline ( opt ) namespace attribut-spécificateur-seq identifiant ( opt ) { declaration-seq } / * depuis C ++ 17 * /
  • espace de noms enclosing-namespace-specifier :: identifier { declaration-seq } / * depuis C ++ 17 * /
  • identifiant d' espace de nommage = spécificateur-espace-nom qualifié ;
  • using namespace-name-spécificateur imbriqué (opt) namespace-name;
  • attribute-specifier-seq utilisant un espace de nommage nom-nom-spécificateur ( opt ) nom-espace ; / * depuis C ++ 11 * /

Remarques

L' namespace mot clé a trois significations différentes selon le contexte:

  1. Lorsqu'il est suivi d'un nom facultatif et d'une séquence de déclarations entourée d'accolades, il définit un nouvel espace de noms ou étend un espace de noms existant avec ces déclarations. Si le nom est omis, l'espace de noms est un espace de noms sans nom .

  2. Lorsqu'il est suivi d'un nom et d'un signe égal, il déclare un alias d'espace de noms .

  3. Lorsqu'elle est précédée par l' using et le suivi d'un nom d'espace de noms, elle forme une directive using , qui permet aux noms dans l'espace de noms donné d'être trouvés par une recherche de noms non qualifiée (mais ne les redéclarent pas dans la portée actuelle). Une directive using ne peut pas exister à la portée de la classe.

using namespace std; est découragé. Pourquoi? Parce que l' namespace std est énorme! Cela signifie qu'il y a de fortes chances que les noms entrent en collision:

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

Que sont les espaces de noms?

Un espace de noms C ++ est un ensemble d'entités C ++ (fonctions, classes, variables), dont les noms sont précédés du nom de l'espace de noms. Lors de l'écriture de code dans un espace de noms, les entités nommées appartenant à cet espace de noms n'ont pas besoin d'être précédées du nom de l'espace de noms, mais les entités extérieures doivent utiliser le nom complet. Le nom qualifié complet a le format <namespace>::<entity> . Exemple:

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.

Les espaces de noms sont utiles pour regrouper les définitions associées. Prenez l'analogie d'un centre commercial. Généralement, un centre commercial est divisé en plusieurs magasins, chaque magasin vendant des articles d'une catégorie spécifique. Un magasin peut vendre des produits électroniques, tandis qu'un autre magasin peut vendre des chaussures. Ces séparations logiques dans les types de magasin aident les acheteurs à trouver les éléments qu'ils recherchent. Les espaces de noms aident les programmeurs c ++, comme les acheteurs, à trouver les fonctions, les classes et les variables qu'ils recherchent en les organisant de manière logique. Exemple:

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

Il y a un seul espace de noms prédéfini, qui est l'espace de noms global qui n'a pas de nom, mais qui peut être noté :: . Exemple:

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

Faire des espaces de noms

Créer un espace de noms est très simple:

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

Pour appeler la bar , vous devez d'abord spécifier l'espace de noms, suivi de l'opérateur de résolution de portée ::

Foo::bar();

Il est permis de créer un espace de noms dans un autre, par exemple:

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

Le code ci-dessus pourrait être simplifié comme suit:

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

Extension des espaces de noms

Une fonctionnalité utile de l' namespace de namespace est que vous pouvez les développer (ajouter des membres).

namespace Foo
{
    void bar() {}
}

//some other stuff

namespace Foo
{
    void bar2() {}
}

Utiliser la directive

Le mot clé "using" a trois saveurs. Combiné avec le mot clé "namespace", vous écrivez une "directive using":

Si vous ne voulez pas écrire Foo:: devant chaque élément de l'espace de noms Foo , vous pouvez utiliser using namespace Foo; importer chaque chose de Foo .

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

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

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

Il est également possible d'importer des entités sélectionnées dans un espace de noms plutôt que dans tout l'espace de noms:

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

Un mot d'avertissement: l' using namespace dans les fichiers d'en-tête est considérée comme un mauvais style dans la plupart des cas. Si cela est fait, l'espace de noms est importé dans chaque fichier qui inclut l'en-tête. Comme il n'y a aucun moyen de « ONU- en using » un espace de noms, cela peut conduire à la pollution de l' espace de noms (symboles plus ou inattendus dans l'espace de noms global) ou, pire, les conflits. Voir cet exemple pour une illustration du problème:

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

Une directive using ne peut pas exister à la portée de la classe.

Recherche dépendante de l'argument

Lors de l'appel d'une fonction sans qualificateur d'espace de nommage explicite, le compilateur peut choisir d'appeler une fonction dans un espace de noms si l'un des types de paramètre de cette fonction se trouve également dans cet espace de noms. Cela s'appelle "Argument Dependent Lookup" ou 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 échoue car aucun de ses types de paramètres ne provient de l'espace de noms Test . call_too fonctionne car SomeClass est membre de Test et par conséquent, il est qualifié pour les règles ADL.

Quand l'ADL ne se produit pas

ADL ne se produit pas si la recherche normale non qualifiée trouve un membre de classe, une fonction qui a été déclarée à portée de bloc ou quelque chose qui n'est pas de type fonction. Par exemple:

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

Espace de noms en ligne

C ++ 11

inline namespace inclut le contenu de l’espace de noms inséré dans l’espace de nom

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

est principalement équivalent à

namespace Outer
{

    namespace Inner
    {
        void foo();
    }

    using Inner::foo;
}

mais l'élément de Outer::Inner:: et ceux associés à Outer:: sont identiques.

Donc, suivre est équivalent

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

L'alternative using namespace Inner; ne serait pas équivalent pour certaines parties délicates en tant que spécialisation de modèle:

Pour

#include <outer.h> // See below

class MyCustomType;
namespace Outer
{
    template <>
    void foo<MyCustomType>() { std::cout << "Specialization"; }
}
  • L'espace de noms en ligne permet la spécialisation de Outer::foo

    // outer.h
    // include guard omitted for simplification
    
    namespace Outer
    {
        inline namespace Inner
        {
            template <typename T>
            void foo() { std::cout << "Generic"; }
        }
    }
    
  • Considérant que l' using namespace ne permet pas la spécialisation de 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`.
    }
    

L'espace de nommage en ligne est un moyen de permettre à plusieurs versions de cohabiter et de passer à la version en inline

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

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

}

Et avec l'usage

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

Espace de noms anonyme / anonyme

Un espace de nom sans nom peut être utilisé pour garantir que les noms ont un lien interne (qui ne peut être référencé que par l'unité de traduction actuelle). Un tel espace de noms est défini de la même manière que tout autre espace de noms, mais sans le nom:

namespace {
    int foo = 42;
}

foo n'est visible que dans l'unité de traduction dans laquelle il apparaît.

Il est recommandé de ne jamais utiliser d'espaces de noms non nommés dans les fichiers d'en-tête, car cela donne une version du contenu de chaque unité de traduction dans laquelle elle est incluse. Ceci est particulièrement important si vous définissez des globales non const.

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

Espaces de noms imbriqués compacts

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

Vous pouvez entrer à la fois l' a et b namespaces en une seule étape avec namespace a::b à partir de 17 C ++.

Aliasing d'un long espace de noms

Ceci est généralement utilisé pour renommer ou raccourcir de longues références d'espace de noms, faisant référence aux composants d'une bibliothèque.

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

Portée de déclaration d'alias

La déclaration d'alias est affectée par l' utilisation des instructions précédentes

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


using namespace boost;

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

Cependant, il est plus facile de se perdre dans l’espace de noms que vous utilisez lorsque vous avez quelque chose comme ça:

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 ​​d'espace de noms

Un espace de noms peut recevoir un alias ( c.-à-d. Un autre nom pour le même espace de noms) en utilisant l' identificateur d' namespace = syntaxe. Les membres de l'espace de noms aliasé sont accessibles en les qualifiant avec le nom de l'alias. Dans l'exemple suivant, l'espace de noms imbriqué AReallyLongName::AnotherReallyLongName n'est pas pratique à taper, donc la fonction qux déclare localement un alias N Les membres de cet espace de noms peuvent alors être accédés simplement en utilisant 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
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow