web-dev-qa-db-fra.com

Utilisation de l'espace de noms std

Il semble y avoir différentes opinions sur l'utilisation de 'using' par rapport à l'espace de noms std.

Certains disent utiliser 'using namespace std', d'autres non, mais préfixent les fonctions std à utiliser avec 'std::', tandis que d'autres disent utiliser quelque chose comme ceci:

using std::string;
using std::cout;
using std::cin;
using std::endl;
using std::vector;

pour toutes les fonctions std à utiliser.

Quels sont les avantages et les inconvénients de chacun?

97
paoloricardo

La plupart des utilisateurs de C++ sont plutôt contents de lire std::string, std::vector, etc. En fait, voir une variable vector me fait me demander s'il s'agit du std::vector ou d'une autre variable vector définie par l'utilisateur.

Je suis toujours contre en utilisant using namespace std;. Il importe toutes sortes de noms dans l'espace de noms global et peut générer toutes sortes d'ambiguïtés non évidentes.

Voici quelques identificateurs communs qui se trouvent dans l’espace de nom std: count, sort, find, equal, reverse. Avoir une variable locale appelée count signifie que using namespace std ne vous permettra pas d'utiliser count au lieu de std::count.

L'exemple classique d'un conflit de noms non souhaité ressemble à ce qui suit. Imaginez que vous êtes un débutant et que vous ne connaissez pas std::count. Imaginez que vous utilisiez quelque chose d'autre dans <algorithm> ou qu'il ait été tiré par un en-tête apparemment sans rapport.

#include <algorithm>
using namespace std;

int count = 0;

int increment()
{
    return ++count; // error, identifier count is ambiguous
}

L'erreur est généralement longue et peu conviviale, car std::count est un modèle avec certains types imbriqués longs.

Ceci est cependant correct, car std::count entre dans l'espace de noms global et le nombre de fonctions le cache.

#include <algorithm>
using namespace std;

int increment()
{
    static int count = 0;
    return ++count;
}

Peut-être un peu étonnamment, c'est OK. Les identifiants importés dans une portée déclarative apparaissent dans l'espace de noms commun qui englobe à la fois leur lieu de définition et leur lieu d'importation. En d'autres termes, std::count est visible en tant que count dans l'espace de noms global, mais uniquement dans increment.

#include <algorithm>

int increment()
{
    using namespace std;
    static int count = 0;
    return ++count;
}

Et pour des raisons similaires, count est ambigu ici. using namespace std ne cause pas std::count, masquez la count extérieure comme on pouvait s'y attendre. La règle using namespace signifie que std::count a l'air (dans la fonction increment) comme s'il avait été déclaré à la portée globale, c'est-à-dire à la même portée que int count = 0; et causait ainsi l'ambiguïté.

#include <algorithm>

int count = 0;

int increment()
{
    using namespace std;
    return ++count; // error ambiguous
}
120
CB Bailey

Exclure les bases (avoir à ajouter std :: infront de tous les objets/fonctions stl et moins de risque de conflit si vous n'avez pas 'using namespace std')

Il est également intéressant de noter que vous ne devriez jamais mettre 

using namespace std

Dans un fichier d'en-tête, comme il peut se propager à tous les fichiers contenant ce fichier d'en-tête, même s'ils ne veulent pas utiliser cet espace de noms.

Dans certains cas, il est très avantageux d’utiliser des éléments tels que

using std::swap

Comme s'il existait une version spécialisée de swap, le compilateur l'utilisera, sinon il retombera sur std :: swap

Si vous appelez std :: swap, vous utilisez toujours la version de base, qui n'appellera pas la version optimisée (si elle existe).

39
Yacoby

Tout d'abord, une terminologie:

  • using-declaration: using std::vector;
  • directive-utilisatrice: using namespace std;

Je pense que l’utilisation de using-directives convient, à condition qu’elles ne soient pas utilisées au niveau global 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, il 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 toute une série de qualificatifs std:: - cela devient simplement un tas de bruit visuel. Cependant, si vous n'utilisez pas tout un tas de noms de l'espace de noms std dans votre code, je ne vois également aucun problème à laisser de côté la directive. C'est une tautologie - si la directive n'est pas nécessaire, il n'est pas nécessaire de l'utiliser. 

De même, si vous pouvez vous débrouiller avec quelques using-declarations (au lieu de using-directives ) pour des types specfic dans l'espace de noms std, il n'y a aucune raison que vous n'ayez pas simplement ces noms spécifiques apportés. dans l'espace de noms actuel. Dans le même ordre d'idées, je pense qu'il serait insensé de compter 25 ou 30 déclarations d'utilisation lorsqu'une seule directive d'utilisation ferait l'affaire.

Il est également bon de garder à l’esprit que, parfois, vous devez utiliser une déclaration using. Reportez-vous à la "Rubrique 25 de Scott Meyers: Envisagez la prise en charge d'un échange sans lancement" dans Effective C++, Third Edition. Pour qu'une fonction générique basée sur un modèle utilise la meilleure méthode de swap pour un type paramétré, vous devez utiliser une recherche using-declaration et une recherche dépendante d'un argument (ou ADL ou recherche de Koenig):

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 nous pencher sur les idiomes communs à diverses langues qui utilisent beaucoup les espaces de noms. Par exemple, Java et C # utilisent les espaces de noms dans une large mesure (peut-être plus que C++). La manière la plus courante d'utiliser les noms dans les espaces de noms dans ces langues consiste à les insérer en masse dans le champ d'application actuel avec l'équivalent d'une directive using. Cela ne cause pas de problèmes généralisés, et les rares fois où il s'agit d'un problème sont traités de manière "exceptionnelle" en traitant les noms en question via des noms qualifiés complets ou par des alias - comme on peut le faire en C++.

Herb Sutter et Andrei Alexandrescu ont ceci à dire à la "Rubrique 59: N'écrivez pas les espaces de noms dans un fichier d'en-tête ou avant une #include" de leur livre, Normes de codage C++: 101 Règles, directives et meilleures pratiques:

En bref: vous pouvez et devez utiliser un espace de noms en utilisant des déclarations et des directives de manière libérale dans vos fichiers d'implémentation après les directives #include et vous en sentez le sentiment. En dépit des affirmations répétées du contraire, les déclarations utilisant des déclarations et des directives ne sont pas pervers et ne vont pas à l'encontre du but des espaces de noms. Ce sont 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 "Le langage de programmation C++, troisième édition". Il dit effectivement cela (C.14 [15]), mais renvoie au chapitre C.10.1 où il dit:

Un using-declaration ajoute un nom à un portée locale. Une directive using-directive fait ne pas; il rend simplement des 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é Par une déclaration ordinaire ou Par une déclaration-using) masque les noms non locaux déclarations du même nom, et toute autre les surcharges illégales du nom sont détecté au point de déclaration.

Notez l'erreur d'ambiguïté pour k++ dans f1(). Les noms globaux ne sont pas donnés préférence sur les noms des espaces de noms rendu accessible dans le cadre global . Ceci fournit une protection significative contre les conflits de noms accidentels, et – important - assure qu'il y a aucun avantage à tirer de polluer l'espace de noms global.

Lorsque les bibliothèques déclarent plusieurs noms sont rendus accessibles via en utilisant des directives, c’est une importante avantage que les affrontements de noms inutilisés ne sont pas considérés comme des erreurs.

...

J'espère voir une diminution radicale de l'utilisation de noms globaux dans new programmes utilisant des espaces de noms comparés à programmes traditionnels C et C++. Le les règles pour les espaces de noms étaient spécifiquement conçu pour ne donner aucun avantage à un Utilisateur «paresseux» des noms globaux sur quelqu'un qui prend soin de ne pas polluer la portée globale.

Et comment a-t-on le même avantage qu'un "utilisateur paresseux de noms globaux"? En tirant parti de la directive using, qui safe met les noms dans un espace de noms à la portée actuelle.

Notez qu’il existe une distinction - les noms dans l’espace de noms std mis à la disposition d’une étendue utilisant correctement une directive using (en plaçant la directive après le #includes) ne ne pas polluent l’espace de noms global. Il s'agit simplement de rendre ces noms disponibles facilement et avec une protection continue contre les affrontements.

23
Michael Burr

N'utilisez jamais l'utilisation d'un espace de noms à portée globale dans un fichier d'en-tête. Cela peut entraîner un conflit et la personne en charge du fichier dans lequel le conflit apparaît n'a aucun contrôle sur la cause.

Dans le fichier d'implémentation, les choix sont beaucoup moins bien coupés.

  • Mettre un espace de nom using std apporte tous les symboles de cet espace de nom. Cela peut être gênant, car pratiquement personne ne connaît tous les symboles présents (il est donc impossible d'appliquer une politique de non-conflit) sans parler des symboles qui seront ajoutés. Et la norme C++ permet à un en-tête d'ajouter des symboles à partir d'autres en-têtes (le C ne le permet pas). Cela peut encore fonctionner dans la pratique pour simplifier l’écriture dans des cas contrôlés. Et si une erreur survient, elle est détectée dans le fichier présentant le problème. 

  • Mettre en utilisant std :: name; a l'avantage de la simplicité d'écriture sans le risque d'importer des symboles inconnus. Le coût est que vous devez importer explicitement tous les symboles recherchés.

  • Les qualifications explicites ajoutent un peu de fouillis, mais je pense que c’est la moins difficile de s’entraîner. 

Dans mon projet, j'utilise une qualification explicite pour tous les noms, j'accepte l'utilisation de std :: name, je me bats Contre l'utilisation de namespace std (nous avons un interpréteur LISP qui a son propre type de liste et le conflit est une chose sûre).

Pour les autres espaces de noms, vous devez également prendre en compte les conventions de dénomination utilisées. Je connais un projet qui utilise un espace de noms (pour la traduction) et un préfixe sur les noms. Faire un using namespace X est alors presque sans risque et ne pas le faire conduit à un code stupide ressemblant à PrefixNS::pfxMyFunction(...).

Dans certains cas, vous souhaitez importer les symboles. std :: swap est le cas le plus courant: vous importez std :: swap puis utilisez swap sans réserve. La recherche dépendante de l'argument trouvera un échange adéquat dans l'espace de noms du type s'il en existe un et retournera au modèle standard s'il n'en existe pas.


Modifier:

Dans les commentaires, Michael Burr se demande si les conflits se produisent dans le monde réel. Voici un véritable exemple live. Nous avons une langue d'extension avec un dialecte LISP. Notre interprète a un fichier include, LISP.h contenant

typedef struct list {} list;

Nous avons dû intégrer et adapter du code (que je nommerai "moteur") qui ressemblait à ceci:

#include <list>
...
using std::list;
...
void foo(list const&) {}

Nous avons donc modifié comme ceci:

#include <list>

#include "module.h"
...
using std::list;
...
void foo(list const&) {}

Bien. Tout fonctionne. Quelques mois plus tard, "module.h" a été modifié pour inclure "list.h". Les tests ont réussi. "module" n'ayant pas été modifié de manière à affecter son ABI, la bibliothèque "engine" peut être utilisée sans recompiler ses utilisateurs. Les tests d'intégration étaient OK. Nouveau "module" publié. La compilation suivante du moteur est tombée en panne lorsque son code n'a pas été modifié.

17
AProgrammer

Tous les deux

using std::string;

et

using namespace std;

ajoutez quelques symboles (un ou plusieurs) à l'espace de noms global. Et ajouter des symboles à un espace de noms global est une chose que vous devriez jamais faire dans les fichiers d’en-tête. Vous n'avez aucun contrôle sur qui va inclure votre en-tête, il y a beaucoup d'en-têtes qui incluent d'autres en-têtes (et des en-têtes qui incluent des en-têtes comprenant des en-têtes, etc.). 

Dans les fichiers d'implémentation (.cpp), c'est à vous de choisir (rappelez-vous seulement de le faire après toutes les directives #include). Vous ne pouvez séparer que le code de ce fichier spécifique, ce qui facilite la gestion et permet d'identifier la raison du conflit de noms. Si vous préférez utiliser std :: (ou tout autre préfixe, il peut y avoir plusieurs espaces de noms dans votre projet) avant les identifiants, tout va bien. Si vous souhaitez ajouter des identifiants que vous utilisez dans un espace de noms global, c'est OK. Si vous voulez apporter un espace de noms complet sur votre tête :-), c'est à vous de décider. Bien que les effets soient limités à une seule unité de compilation, ils sont acceptables.

4
Tadeusz Kopec

Pour moi, je préfère utiliser :: lorsque cela est possible.

std::list<int> iList;

Je déteste écrire:

for(std::list<int>::iterator i = iList.begin(); i != iList.end(); i++)
{
    //
}

J'espère que j'écrirais ceci avec C++ 0x:

for(auto i = iList.begin(); i != iList.end(); i++)
{
    //
}

Si l'espace de noms est très long,

namespace dir = boost::filesystem;

dir::directory_iterator file("e:/boost");
dir::directory_iterator end;

for( ; file != end; file++)
{
    if(dir::is_directory(*file))
        std::cout << *file << std::endl;
}
3
AraK

Si votre code ne risque pas d'être en conflit de nom avec std et d'autres bibliothèques, vous pouvez utiliser:

using namespace std;

Mais si vous voulez connaître avec précision la dépendance de votre code pour la documentation ou s'il existe un risque de conflits de noms, utilisez l'inverse:

using std::string;
using std::cout;

La troisième solution, n'utilisez pas ces solutions et écrivez std :: avant chaque utilisation dans le code vous apporte plus de sécurité mais peut-être un peu de lourdeur dans le code ... 

3
Matthieu

Vous ne devriez jamais être using namespace std à la portée d'un espace de noms dans un en-tête. De plus, je suppose que la plupart des programmeurs se demanderont quand ils verront vector ou string sans std::, alors je pense que non, using namespace std est préférable. Par conséquent, je plaide pour ne jamais être using namespace std du tout. 

Si vous vous en sentez obligé, ajoutez des déclarations locales utilisant using std::vector. Mais demandez-vous: qu'est-ce que cela vaut? Une ligne de code est écrite une fois (peut-être deux fois), mais elle est lue dix, cent ou mille fois. L'effort de saisie enregistré, qui consiste à ajouter une déclaration ou une directive using, est marginal par rapport à l'effort de lecture du code. 

Dans cet esprit, dans un projet il y a dix ans, nous avons décidé de qualifier explicitement tous les identificateurs avec leurs noms d'espaces de noms complets. Ce qui semblait gênant au début devint la routine en deux semaines. Désormais, dans tous les projets de cette société, personne n’utilise plus de directives ou de déclarations. (À une exception près, voir ci-dessous.) En regardant le code (plusieurs MLoC) après dix ans, j'ai l'impression que nous avons pris la bonne décision. 

J'ai constaté que ceux qui s'opposent à l'interdiction de using ne l'utilisent généralement pas pour un seul projet. Ceux qui ont essayé trouvent souvent qu'il vaut mieux que d'utiliser des directives/déclarations après une très courte période. 

Remarque: La seule exception est using std::swap qui est nécessaire (en particulier dans le code générique) pour récupérer les surcharges de swap() qui ne peuvent pas être placées dans l'espace de noms std (car nous ne sommes pas autorisés à placer des surcharges de fonctions std dans cet espace de noms). 

2
sbi

Les espaces de noms gardent le code contenu pour éviter confusion et pollution des signatures de fonction.

Here est une démonstration complète et documentée de propernamespace usage:

#include <iostream>
#include <cmath>  // Uses ::log, which would be the log() here if it were not in a namespace, see https://stackoverflow.com/questions/11892976/why-is-my-log-in-the-std-namespace

// Silently overrides std::log
//double log(double d) { return 420; }

namespace uniquename {
    using namespace std;  // So we don't have to waste space on std:: when not needed.

    double log(double d) {
        return 42;
    }

    int main() {
        cout << "Our log: " << log(4.2) << endl;
        cout << "Standard log: " << std::log(4.2);
        return 0;
    }
}

// Global wrapper for our contained code.
int main() {
    return uniquename::main();
}

Sortie:

Our log: 42
Standard log: 1.43508
1
Cees Timmerman

using namespace std importe le contenu de l'espace de noms std dans celui-ci. Ainsi, l’avantage est que vous n’aurez pas à taper std:: devant toutes les fonctions de cet espace de noms. Cependant, il se peut que vous disposiez d'espaces de nommage différents dotés de fonctions du même nom. Ainsi, vous pouvez finir par ne pas appeler celui que vous voulez.

Spécifier manuellement ceux que vous voulez importer dans std empêche cela, mais peut entraîner une longue liste d’utilisations au début de votre fichier, ce que certains développeurs trouveront laids;)!

Personnellement, je préfère spécifier l’espace de noms chaque fois que j’utilise une fonction, sauf si l’espace de noms est trop long, auquel cas je mets un en utilisant au début du fichier.

EDIT: comme indiqué dans une autre réponse, vous ne devriez jamais mettre un using namespace dans un fichier d'en-tête, car il se propage à tous les fichiers incluant cet en-tête et peut donc produire un comportement indésirable.

EDIT2: corrigé ma réponse, merci à Charles de commenter.

1
Wookai

Cette discussion restera active aussi longtemps que le IDE avec lequel vous travaillez n'est pas suffisamment souple pour afficher ou masquer les informations exactes dont vous avez besoin.

En effet, votre code dépend de la tâche à accomplir.

Lors de la création de mon code source, je préfère voir exactement quelle classe j'utilise: est-ce std::string ou la classe BuzFlox::Obs::string?

Lors de la conception du flux de contrôle, je ne suis même pas intéressé par les types de variables, mais je souhaite mettre l'accent sur les variables if et while et continue.

C'est donc mon conseil:

En fonction de l'audience de votre code et de la puissance de vos outils, choisissez la méthode la plus facile à lire ou la plupart des informations.

0
xtofl

Tout comme en Java, vous pouvez utiliser soit Java.util. *, Soit simplement sélectionner chaque classe individuellement, cela dépend du style. Notez que vous ne voulez pas d'un using namespace std au début de votre étendue de fichier/étendue, car vous pollueriez l'espace de noms et risqueriez des conflits, ce qui neutraliserait le point d'espaces de noms. Mais si vous avez une fonction qui utilise beaucoup de STL, elle encombrera le code pour contenir une syntaxe de syntaxe préfixante dans votre logique et vous devriez probablement envisager d'utiliser soit using namespace std (lorsque vous utilisez une variété de classes), soit un nom individuel usings (lorsque vous utilisez peu de cours souvent).

0
Sam Brightman

Il y a plusieurs façons de résoudre ce problème.

Premièrement: utilisez comme ce que vous avez fait.

Deuxièmement: do namespace S = std;, en réduisant 2 caractères.

Troisièmement: utilisez static.

Quatrièmement: n'utilisez pas les noms que std utilise.

0
user5058091