Namespaces
Les espaces de noms fournissent une méthode pour prévenir les conflits de noms dans les grands projets.
Les entités déclarées à l'intérieur d'un bloc de namespace sont placées dans une portée de namespace, ce qui empêche qu'elles soient confondues avec des entités de même nom dans d'autres portées.
Les entités déclarées en dehors de tous les blocs de namespace appartiennent au
namespace global
. Le namespace global appartient à la
portée globale
, et peut être référencé explicitement avec un
::
précédent. Bien qu'il n'ait pas de déclaration, le namespace global n'est pas un
namespace sans nom
.
Plusieurs blocs de namespace portant le même nom sont autorisés. Toutes les déclarations à l'intérieur de ces blocs sont déclarées dans la même portée de namespace.
Table des matières |
Syntaxe
namespace
ns-name
{
déclarations
}
|
(1) | ||||||||
inline
namespace
ns-name
{
déclarations
}
|
(2) | (depuis C++11) | |||||||
namespace
{
déclarations
}
|
(3) | ||||||||
ns-name
::
member-name
|
(4) | ||||||||
using
namespace
ns-name
;
|
(5) | ||||||||
using
ns-name
::
member-name
;
|
(6) | ||||||||
namespace
name
=
qualified-namespace
;
|
(7) | ||||||||
namespace
ns-name
::
member-name
{
déclarations
}
|
(8) | (depuis C++17) | |||||||
namespace
ns-name
::
inline
member-name
{
déclarations
}
|
(9) | (depuis C++20) | |||||||
Explication
Espaces de noms
inline
(facultatif)
namespace
attr
(facultatif)
identifiant
{
corps-du-namespace
}
|
|||||||||
` n'est pas traduit
- Les termes spécifiques C++ (`inline`, `namespace`) sont conservés
- Seul le texte en dehors des balises `
` a été traduit en français
- La mise en forme originale est préservée
inline
|
- |
(depuis C++11)
si présent, en fait un espace de noms en ligne (voir ci-dessous). Ne peut pas apparaître dans la
définition d'extension d'espace de noms
si la
définition d'origine d'espace de noms
n'utilisait pas
inline
|
||
| attr | - | (depuis C++17) séquence facultative d'un nombre quelconque d' attributs | ||
| identifier | - |
soit
|
||
| namespace-body | - | séquence éventuellement vide de déclarations de tout type (y compris les définitions de classes et de fonctions ainsi que les espaces de noms imbriqués) |
Les définitions d'espace de noms sont uniquement autorisées au niveau de la portée de l'espace de noms, y compris la portée globale.
Pour rouvrir un espace de noms existant (formellement, pour être une définition d'extension d'espace de noms ), la recherche de l' identifiant utilisé dans la définition de l'espace de noms doit résoudre en un nom d'espace de noms (et non un alias d'espace de noms), qui a été déclaré comme membre de l'espace de noms englobant ou d'un espace de noms inline dans un espace de noms englobant.
Le namespace-body définit une portée d'espace de noms , qui affecte la recherche de nom .
Tous les noms introduits par les déclarations qui apparaissent dans le namespace-body (y compris les définitions de namespace imbriquées) deviennent membres du namespace identifier , que cette définition de namespace soit la définition de namespace originale (qui a introduit identifier ), ou une définition de namespace d'extension (qui a "rouvert" le namespace déjà défini)
Un membre de namespace qui a été déclaré dans un corps de namespace peut être défini ou redéclaré en dehors de celui-ci en utilisant une qualification explicite
namespace Q { namespace V // V est un membre de Q, et est entièrement défini dans Q { // namespace Q::V { // Alternative C++17 aux lignes ci-dessus class C { void m(); }; // C est un membre de V et est entièrement défini dans V // C::m est seulement déclaré void f(); // f est un membre de V, mais est seulement déclaré ici } void V::f() // définition du membre f de V en dehors de V // les espaces de noms englobants de f sont toujours l'espace de noms global, Q, et Q::V { extern void h(); // Ceci déclare ::Q::V::h } void V::C::m() // définition de V::C::m en dehors de l'espace de noms (et du corps de la classe) // les espaces de noms englobants sont l'espace de noms global, Q, et Q::V {} }
Les définitions et redéclarations hors espace de noms sont uniquement autorisées
- après le point de déclaration,
- au niveau de la portée du namespace, et
- dans les namespaces qui englobent le namespace d'origine (y compris le namespace global).
De plus, ils doivent utiliser la syntaxe d'identifiant qualifié.
namespace Q { namespace V // définition originale de l'espace de noms pour V { void f(); // déclaration de Q::V::f } void V::f() {} // Correct void V::g() {} // Erreur : g() n'est pas encore un membre de V namespace V // définition d'extension de l'espace de noms pour V { void g(); // déclaration de Q::V::g } } namespace R // n'est pas un espace de noms englobant pour Q { void Q::V::g() {} // Erreur : impossible de définir Q::V::g dans R } void Q::V::g() {} // Correct : l'espace de noms global englobe Q
Les noms introduits par les déclarations friend dans une classe non locale X deviennent membres de l'espace de noms englobant le plus proche de X, mais ils ne deviennent pas visibles pour la recherche de nom ordinaire (ni non qualifiée ni qualifiée ) à moins qu'une déclaration correspondante ne soit fournie au niveau de la portée de l'espace de noms, soit avant soit après la définition de la classe. Un tel nom peut être trouvé via ADL qui prend en compte à la fois les espaces de noms et les classes.
Seule la namespace englobante la plus interne est prise en compte par une telle déclaration d'amitié lorsqu'il s'agit de déterminer si le nom entrerait en conflit avec un nom précédemment déclaré.
void h(int); namespace A { class X { friend void f(X); // A::f est un ami class Y { friend void g(); // A::g est un ami friend void h(int); // A::h est un ami, pas de conflit avec ::h }; }; // A::f, A::g et A::h ne sont pas visibles au niveau du namespace // bien qu'ils soient membres du namespace A X x; void g() // définition de A::g { f(x); // A::X::f est trouvé via ADL } void f(X) {} // définition de A::f void h(int) {} // définition de A::h // A::f, A::g et A::h sont maintenant visibles au niveau du namespace // et ils sont aussi amis de A::X et A::X::Y }
Espaces de noms inline
Un espace de noms inline est un espace de noms qui utilise le mot-clé optionnel
Les membres d'un espace de noms inline sont traités comme s'ils étaient membres de l'espace de noms englobant dans de nombreuses situations (énumérées ci-dessous). Cette propriété est transitive : si un espace de noms N contient un espace de noms inline M, qui contient à son tour un espace de noms inline O, alors les membres de O peuvent être utilisés comme s'ils étaient membres de M ou N.
// in C++14, std::literals and its member namespaces are inline { using namespace std::string_literals; // makes visible operator""s // from std::literals::string_literals auto str = "abc"s; } { using namespace std::literals; // makes visible both // std::literals::string_literals::operator""s // and std::literals::chrono_literals::operator""s auto str = "abc"s; auto min = 60s; } { using std::operator""s; // makes both std::literals::string_literals::operator""s // and std::literals::chrono_literals::operator""s visible auto str = "abc"s; auto min = 60s; } Note : la règle concernant les spécialisations permet le versionnage des bibliothèques : différentes implémentations d'un modèle de bibliothèque peuvent être définies dans différents espaces de noms inline, tout en permettant à l'utilisateur d'étendre l'espace de noms parent avec une spécialisation explicite du modèle principal :
Exécuter ce code
namespace Lib { inline namespace Lib_1 { template<typename T> class A; } template<typename T> void g(T) { /* ... */ } } /* ... */ struct MyClass { /* ... */ }; namespace Lib { template<> class A<MyClass> { /* ... */ }; } int main() { Lib::A<MyClass> a; g(a); // ok, Lib is an associated namespace of A } |
(depuis C++11) |
Espaces de noms non nommés
La unnamed-namespace-definition est une définition d'espace de noms de la forme
inline
(facultatif)
namespace
attr
(facultatif)
{
namespace-body
}
|
|||||||||
inline
|
- | (since C++11) si présent, fait de ceci un espace de noms inline |
| attr | - | (since C++17) séquence facultative d'un nombre quelconque d' attributs |
Cette définition est traitée comme une définition d'un espace de noms avec un nom unique et une using-directive dans la portée courante qui nomme cet espace de noms sans nom (Note : la directive using implicitement ajoutée rend l'espace de noms disponible pour la recherche qualifiée et la recherche non qualifiée , mais pas pour la recherche dépendante des arguments ). Le nom unique est unique sur l'ensemble du programme, mais au sein d'une unité de traduction, chaque définition d'espace de noms sans nom correspond au même nom unique : plusieurs définitions d'espaces de noms sans nom dans la même portée désignent le même espace de noms sans nom.
namespace { int i; // définit ::(unique)::i } void f() { i++; // incrémente ::(unique)::i } namespace A { namespace { int i; // A::(unique)::i int j; // A::(unique)::j } void g() { i++; } // A::(unique)::i++ } using namespace A; // introduit tous les noms de A dans l'espace de noms global void h() { i++; // erreur : ::(unique)::i et ::A::(unique)::i sont tous deux dans la portée A::i++; // ok, incrémente ::A::(unique)::i j++; // ok, incrémente ::A::(unique)::j }
|
Bien que les noms dans un espace de noms sans nom puissent être déclarés avec une liaison externe, ils ne sont jamais accessibles depuis d'autres unités de traduction car leur nom d'espace de noms est unique. |
(until C++11) |
|
Les espaces de noms sans nom ainsi que tous les espaces de noms déclarés directement ou indirectement dans un espace de noms sans nom ont une liaison interne , ce qui signifie que tout nom déclaré dans un espace de noms sans nom possède une liaison interne. |
(since C++11) |
Déclarations using
Introduit un nom qui est défini ailleurs dans la région déclarative où cette déclaration using apparaît.
using
typename
(optionnel)
spécificateur-de-nom-imbriqué
identifiant-non-qualifié
;
|
(jusqu'en C++17) | ||||||||
using
liste-de-déclarateurs
;
|
(depuis C++17) | ||||||||
typename
|
- |
le mot-clé
typename
peut être utilisé si nécessaire pour résoudre les
noms dépendants
, lorsque la déclaration using introduit un type membre d'une classe de base dans un modèle de classe
|
| nested-name-specifier | - |
une séquence de noms et d'opérateurs de résolution de portée
::
, se terminant par un opérateur de résolution de portée. Un simple
::
fait référence à l'espace de noms global.
|
| unqualified-id | - | une expression d'identifiant |
| declarator-list | - |
liste séparée par des virgules d'un ou plusieurs déclarateurs de la forme
typename
(optionnel)
nested-name-specifier
unqualified-id
. Un déclarateur peut être suivi d'une ellipse pour indiquer une
expansion de paquet
, bien que cette forme ne soit significative que dans les
définitions de classes dérivées
|
Les using-declarations peuvent être utilisées pour introduire des membres de namespace dans d'autres namespaces et portées de bloc, ou pour introduire des membres de classe de base dans les définitions de classes dérivées , ou pour introduire des enumerators dans les namespaces, portées de bloc et classes (since C++20) .
|
Une déclaration using avec plusieurs using-déclarateurs est équivalente à une séquence correspondante de déclarations using avec un seul using-déclarateur. |
(depuis C++17) |
Pour l'utilisation dans les définitions de classes dérivées, voir using declaration .
Les noms introduits dans la portée d'un espace de noms par une using-declaration peuvent être utilisés comme n'importe quels autres noms, y compris la recherche qualifiée depuis d'autres portées :
void f(); namespace A { void g(); } namespace X { using ::f; // la fonction globale f est maintenant visible comme ::X::f using A::g; // A::g est maintenant visible comme ::X::g using A::g, A::g; // (C++17) OK : double déclaration autorisée au niveau de la portée du namespace } void h() { X::f(); // appelle ::f X::g(); // appelle A::g }
Si, après qu'une déclaration using a été utilisée pour prendre un membre d'un espace de noms, l'espace de noms est étendu et que des déclarations supplémentaires pour le même nom sont introduites, ces déclarations supplémentaires ne deviennent pas visibles via la déclaration using (contrairement à la directive using). Une exception survient lorsqu'une déclaration using nomme un modèle de classe : les spécialisations partielles introduites ultérieurement sont effectivement visibles, car leur lookup s'effectue via le modèle primaire.
namespace A { void f(int); } using A::f; // ::f est maintenant un synonyme de A::f(int) namespace A // extension de l'espace de noms { void f(char); // ne modifie pas la signification de ::f } void foo() { f('a'); // appelle f(int), même si f(char) existe. } void bar() { using A::f; // ce f est un synonyme à la fois pour A::f(int) et A::f(char) f('a'); // appelle f(char) }
Les using-declarations ne peuvent pas nommer template-id , ou un namespace , ou un énumérateur scopé (jusqu'à C++20) . Chaque déclarateur dans une using-declaration introduit un et un seul nom, par exemple une using-declaration pour une énumération n'introduit aucun de ses énumérateurs.
Toutes les restrictions sur les déclarations régulières des mêmes noms, le masquage et les règles de surcharge s'appliquent aux using-declarations :
namespace A { int x; } namespace B { int i; struct g {}; struct x {}; void f(int); void f(double); void g(char); // OK : le nom de fonction g masque la structure g } void func() { int i; using B::i; // erreur : i déclaré deux fois void f(char); using B::f; // OK : f(char), f(int), f(double) sont surchargées f(3.5); // appelle B::f(double) using B::g; g('a'); // appelle B::g(char) struct g g1; // déclare g1 de type struct B::g using B::x; using A::x; // OK : masque la structure B::x x = 99; // assigne à A::x struct x x1; // déclare x1 de type struct B::x }
Si une fonction a été introduite par une déclaration using, déclarer une fonction avec le même nom et la même liste de paramètres est incorrect (sauf si la déclaration concerne la même fonction). Si un modèle de fonction a été introduit par une déclaration using, déclarer un modèle de fonction avec le même nom, la même liste de types de paramètres, le même type de retour et la même liste de paramètres de modèle est incorrect. Deux déclarations using peuvent introduire des fonctions avec le même nom et la même liste de paramètres, mais si un appel à cette fonction est tenté, le programme est incorrect.
namespace B { void f(int); void f(double); } namespace C { void f(int); void f(double); void f(char); } void h() { using B::f; // introduit B::f(int), B::f(double) using C::f; // introduit C::f(int), C::f(double), et C::f(char) f('h'); // appelle C::f(char) f(1); // erreur : B::f(int) ou C::f(int) ? void f(int); // erreur : f(int) entre en conflit avec C::f(int) et B::f(int) }
Si une entité est déclarée, mais non définie dans un espace de noms interne, puis déclarée via une using-declaration dans l'espace de noms externe, et qu'une définition apparaît ensuite dans l'espace de noms externe avec le même nom non qualifié, cette définition est un membre de l'espace de noms externe et entre en conflit avec la using-declaration :
namespace X { namespace M { void g(); // déclare, mais ne définit pas X::M::g() } using M::g; void g(); // Erreur : tentative de déclarer X::g qui entre en conflit avec X::M::g() }
Plus généralement, une déclaration qui apparaît dans n'importe quelle portée d'espace de noms et introduit un nom en utilisant un identifiant non qualifié introduit toujours un membre dans l'espace de noms où elle se trouve et non dans aucun autre espace de noms. Les exceptions sont les instanciations explicites et les spécialisations explicites d'un template primaire défini dans un espace de noms inline : parce qu'elles n'introduisent pas de nouveau nom, elles peuvent utiliser un identifiant non qualifié dans un espace de noms englobant.
Directives using
Une using-directive est une block-declaration avec la syntaxe suivante :
attr
(optionnel)
using
namespace
spécificateur-de-nom-imbriqué
(optionnel)
nom-d'espace-de-nom
;
|
(1) | ||||||||
| attr | - | (depuis C++11) n'importe quel nombre d' attributs qui s'appliquent à cette directive using |
| nested-name-specifier | - |
une séquence de noms et d'opérateurs de résolution de portée
::
, se terminant par un opérateur de résolution de portée. Un simple
::
fait référence à l'espace de noms global. Lors de la recherche des noms dans cette séquence,
la recherche
ne considère que les déclarations d'espace de noms
|
| namespace-name | - | un nom d'espace de noms. Lors de la recherche de ce nom, la recherche ne considère que les déclarations d'espace de noms |
Les directives using sont autorisées uniquement dans la portée d'un espace de noms scope et dans la portée de bloc. Du point de vue de la recherche de nom non qualifiée de tout nom après une directive using et jusqu'à la fin de la portée dans laquelle elle apparaît, chaque nom du namespace-name est visible comme s'il était déclaré dans l'espace de noms englobant le plus proche qui contient à la fois la directive using et le namespace-name .
La directive using n'ajoute aucun nom à la région déclarative dans laquelle elle apparaît (contrairement à la déclaration using), et n'empêche donc pas la déclaration de noms identiques.
Les directives using sont transitives pour les besoins de la recherche non qualifiée : si une portée contient une directive using qui nomme un namespace-name , qui contient lui-même une directive using pour un certain namespace-name-2 , l'effet est le même que si les directives using du second espace de noms apparaissaient dans le premier. L'ordre dans lequel ces espaces de noms transitifs se produisent n'influence pas la recherche de nom.
namespace A { int i; } namespace B { int i; int j; namespace C { namespace D { using namespace A; // Les noms de A sont "injectés" dans D. // La recherche non qualifiée dans D considère ces noms comme ayant la même // portée que la portée globale (par exemple, pour les besoins de masquage de noms). // La recherche qualifiée se référant à D (D::nom pour un certain nom) // trouvera le même nom que la recherche non qualifiée dans D. int j; int k; int a = i; // i est B::i, car A::i est masqué par B::i int b = ::i; // erreur : il n'y a toujours pas de i dans l'espace de noms global } using namespace D; // les noms de D et A sont injectés dans C int k = 89; // OK de déclarer un nom identique à celui introduit par un using int l = k; // ambigu : C::k ou D::k int m = i; // ok : B::i masque A::i int n = j; // ok : D::j masque B::j } } // Ce sont toutes des définitions équivalentes : int t0 = B::i; int t1 = B::C::a; int t2 = B::C::D::a;
Si, après qu'une directive using a été utilisée pour nommer un espace de noms, l'espace de noms est étendu et que des membres supplémentaires et/ou des directives using y sont ajoutés, ces membres supplémentaires et les espaces de noms supplémentaires sont visibles via la directive using (contrairement à la déclaration using)
namespace D { int d1; void f(char); } using namespace D; // introduit D::d1, D::f, D::d2, D::f, // E::e et E::f dans l'espace de noms global ! int d1; // OK : pas de conflit avec D::d1 lors de la déclaration namespace E { int e; void f(int); } namespace D // extension d'espace de noms { int d2; using namespace E; // directive using transitive void f(int); } void f() { d1++; // erreur : ambiguïté ::d1 ou D::d1 ? ::d1++; // OK D::d1++; // OK d2++; // OK, d2 est D::d2 e++; // OK : e est E::e via le using transitif f(1); // erreur : ambiguïté : D::f(int) ou E::f(int) ? f('a'); // OK : la seule f(char) est D::f(char) }
Notes
La directive using
using
namespace
std
;
à n'importe quelle portée d'espace de noms introduit chaque nom de l'espace de noms
std
dans l'espace de noms global (puisque l'espace de noms global est l'espace de noms le plus proche qui contient à la fois
std
et tout espace de noms déclaré par l'utilisateur), ce qui peut entraîner des collisions de noms indésirables. Cette directive, ainsi que d'autres directives using, sont généralement considérées comme une mauvaise pratique au niveau de la portée de fichier d'un fichier d'en-tête (
SF.7 : N'utilisez pas
using
namespace
au niveau global dans un fichier d'en-tête
).
| Macro de test de fonctionnalité | Valeur | Std | Fonctionnalité |
|---|---|---|---|
__cpp_namespace_attributes
|
201411L
|
(C++17) | Attributs pour les espaces de noms |
Mots-clés
Exemple
Cet exemple montre comment utiliser un espace de noms pour créer une classe qui a déjà été nommée dans l'espace de noms
std
.
#include <vector> namespace vec { template<typename T> class vector { // ... }; } // of vec int main() { std::vector<int> v1; // Standard vector. vec::vector<int> v2; // User defined vector. // v1 = v2; // Error: v1 and v2 are different object's type. { using namespace std; vector<int> v3; // Same as std::vector v1 = v3; // OK } { using vec::vector; vector<int> v4; // Same as vec::vector v2 = v4; // OK } }
Rapports de défauts
Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.
| DR | Appliqué à | Comportement publié | Comportement corrigé |
|---|---|---|---|
| CWG 101 | C++98 |
le programme est mal formé si une déclaration de fonction dans la portée
d'un espace de noms ou d'un bloc et une fonction introduite par une using-declaration déclarent la même fonction (aucune ambiguïté) |
autorisé |
| CWG 373 | C++98 |
la recherche ne considérait les déclarations d'espace de noms que pour
le dernier nom dans l'opérande d'une using-directive (ce qui est sous-optimal, car les classes ne peuvent pas contenir d'espaces de noms) |
la restriction de recherche
s'applique à tous les noms dans les opérandes des using-directives |
| CWG 460 | C++98 | une using-declaration pouvait nommer un espace de noms | interdit |
| CWG 565 | C++98 |
une using-declaration ne peut pas introduire une fonction
identique à une autre fonction dans la même portée, mais la restriction n'était pas appliquée aux modèles de fonction |
appliquer la même restriction
aux modèles de fonction également |
| CWG 986 | C++98 | la using-directive était transitive pour la recherche qualifiée | seulement transitive pour la recherche non qualifiée |
| CWG 987 | C++98 |
les entités déclarées dans un espace de noms imbriqué étaient
également membres de l'espace de noms englobant |
portées imbriquées exclues |
| CWG 1021 | C++98 |
il n'était pas clair si une entité dont la définition
est introduite dans un espace de noms via une using-declaration est considérée comme définie dans cet espace de noms |
non définie dans cet espace de noms |
| CWG 1838 | C++98 |
une définition non qualifiée dans un espace de noms externe
pouvait définir une entité déclarée, mais non définie dans un autre espace de noms et importée par une using |
la définition non qualifiée
se réfère toujours à son espace de noms |
| CWG 2155 | C++98 |
la résolution de
CWG issue 1838
n'était pas
appliquée aux déclarations de classes et d'énumérations |
appliquée |
Voir aussi
| namespace alias | crée un alias d'un espace de noms existant |