Tout d'abord, un peu de terminologie:
- using-declaration :
using std::vector;
- using-directive :
using namespace std;
Je pense que l'utilisation de directives d'utilisation est très bien, tant qu'elles ne sont pas utilisées à l'échelle globale dans un fichier d'en-tête. Donc avoir
using namespace std;
dans votre fichier .cpp n'est pas vraiment un problème, et si cela s'avère être, c'est complètement sous votre contrôle (et il peut même être étendu à des blocs particuliers si vous le souhaitez). Je ne vois aucune raison particulière d'encombrer le code avec une multitude de std::
qualificatifs - cela devient juste un tas de bruit visuel. Cependant, si vous n'utilisez pas tout un tas de noms de l' std
espace de noms dans votre code, je ne vois pas non plus de problème à laisser de côté la directive. C'est une tautologie - si la directive n'est pas nécessaire, alors il n'est pas nécessaire de l'utiliser.
De même, si vous pouvez vous en tirer avec quelques déclarations using (au lieu de directives using ) pour des types spécifiques dans l' std
espace de noms, alors il n'y a aucune raison pour laquelle vous ne devriez pas avoir juste ces noms spécifiques dans l'espace de noms actuel. De la même manière, je pense que ce serait fou et compliqué d'avoir 25 ou 30 déclarations d'utilisation alors qu'une seule directive d'utilisation ferait tout aussi bien l'affaire.
Il est également bon de garder à l'esprit qu'il y a des moments où vous devez utiliser une déclaration using. Reportez-vous à «Item 25: Consider support for a non-throwing swap» de Scott Meyers, Third Edition. Pour qu'une fonction générique et basée sur un modèle utilise la `` meilleure '' méthode d'échange pour un type paramétré, vous devez utiliser une déclaration d'utilisation et une recherche dépendante de l'argument (également appelée ADL ou Koenig lookup):
template< typename T >
void foo( T& x, T& y)
{
using std::swap; // makes std::swap available in this function
// do stuff...
swap( x, y); // will use a T-specific swap() if it exists,
// otherwise will use std::swap<T>()
// ...
}
Je pense que nous devrions examiner les expressions idiomatiques communes à divers langages qui utilisent de manière significative les espaces de noms. Par exemple, Java et C # utilisent les espaces de noms dans une large mesure (sans doute plus que C ++). La manière la plus courante d'utiliser les noms dans les espaces de noms dans ces langages est de les intégrer en masse dans la portée actuelle avec l'équivalent d'une directive using. Cela ne pose pas de problèmes très répandus, et les rares fois où c'est un problème sont traités sur une base «d'exception» en traitant les noms en question via des noms complets ou par alias - tout comme cela peut être fait en C ++.
Herb Sutter et Andrei Alexandrescu ont ceci à dire dans «Point 59: N'écrivez pas les utilisations de l'espace de noms dans un fichier d'en-tête ou avant un #include» de leur livre, Normes de codage C ++: 101 règles, directives et meilleures pratiques:
En bref: vous pouvez et devez utiliser l'espace de noms en utilisant des déclarations et des directives libéralement dans vos fichiers d'implémentation après les #include
directives et vous en sentirez bien. Malgré des affirmations répétées du contraire, les espaces de noms utilisant des déclarations et des directives ne sont pas mauvais et ils ne vont pas à l'encontre de l'objectif des espaces de noms. C'est plutôt ce qui rend les espaces de noms utilisables.
Stroupstrup est souvent cité comme disant: "Ne polluez pas l'espace de noms global", dans "The C ++ Programming Language, Third Edition". Il dit effectivement cela (C.14 [15]), mais se réfère au chapitre C.10.1 où il dit:
Une déclaration d'utilisation ajoute un nom à une portée locale. Une directive d'utilisation ne le fait pas; il rend simplement les noms accessibles dans la portée dans laquelle ils ont été déclarés. Par exemple:
namespaceX {
int i , j , k ;
}
int k ;
void f1()
{
int i = 0 ;
using namespaceX ; // make names from X accessible
i++; // local i
j++; // X::j
k++; // error: X::k or global k ?
::k ++; // the global k
X::k ++; // X’s k
}
void f2()
{
int i = 0 ;
using X::i ; // error: i declared twice in f2()
using X::j ;
using X::k ; // hides global k
i++;
j++; // X::j
k++; // X::k
}
Un nom déclaré localement (déclaré soit par une déclaration ordinaire soit par une déclaration d'utilisation) masque les déclarations non locales du même nom, et toute surcharge illégale du nom est détectée au point de déclaration.
Notez l'erreur d'ambiguïté pour k++
in
f1()
. Les noms globaux n'ont pas de préférence sur les noms d'espaces de noms rendus accessibles dans la portée globale. Cela fournit une protection significative contre les conflits de noms accidentels et - surtout - garantit qu'il n'y a aucun avantage à tirer de la pollution de l'espace de noms global.
Lorsque les bibliothèques déclarant de nombreux noms sont rendues accessibles via des directives using, c'est un avantage significatif que les conflits de noms inutilisés ne sont pas considérés comme des erreurs.
...
J'espère voir une diminution radicale de l'utilisation des noms globaux dans les nouveaux programmes utilisant des espaces de noms par rapport aux programmes traditionnels C et C ++. Les règles pour les espaces de noms ont été spécifiquement conçues pour ne donner aucun avantage à un utilisateur «paresseux» de noms globaux par rapport à quelqu'un qui prend soin de ne pas polluer la portée globale.
Et comment avoir le même avantage qu'un «utilisateur paresseux de noms globaux»? En tirant parti de la directive using, qui rend en toute sécurité les noms d'un espace de noms disponibles pour la portée actuelle.
Notez qu'il y a une distinction - les noms dans l' std
espace de noms mis à disposition d'une portée avec l'utilisation correcte d'une directive using (en plaçant la directive après le #includes
) ne polluent pas l'espace de noms global. Il s'agit simplement de rendre ces noms disponibles facilement et avec une protection continue contre les affrontements.