Pourquoi «using namespace std;» considéré comme une mauvaise pratique?
D'autres m'ont dit qu'écrire using namespace std;
dans le code est faux et que je devrais utiliser std::cout
et std::cin
directement à la place.
Pourquoi est-ce using namespace std;
considéré comme une mauvaise pratique? Est-ce inefficace ou risque-t-il de déclarer des variables ambiguës (variables qui partagent le même nom qu'une fonction dans l' std
espace de noms)? Cela a-t-il un impact sur les performances?
Réponses
Ce n'est pas du tout lié à la performance. Mais considérez ceci: vous utilisez deux bibliothèques appelées Foo et Bar:
using namespace foo;
using namespace bar;
Tout fonctionne bien et vous pouvez appeler Blah()
de Foo et Quux()
de Bar sans problème. Mais un jour, vous passez à une nouvelle version de Foo 2.0, qui propose désormais une fonction appelée Quux()
. Vous avez maintenant un conflit: Foo 2.0 et Bar sont importés Quux()
dans votre espace de noms global. Cela demandera un certain effort pour corriger, surtout si les paramètres de la fonction correspondent.
Si vous aviez utilisé foo::Blah()
et bar::Quux()
, alors l'introduction de foo::Quux()
aurait été un non-événement.
Je suis d'accord avec tout ce que Greg a écrit , mais j'aimerais ajouter: cela peut même être pire que ce que Greg a dit!
La bibliothèque Foo 2.0 pourrait introduire une fonction, Quux()
qui correspond sans ambiguïté à certains de vos appels Quux()
que bar::Quux()
votre code appelé pendant des années. Ensuite, votre code se compile toujours , mais il appelle silencieusement la mauvaise fonction et fait Dieu sait quoi. C'est à peu près aussi grave que les choses peuvent devenir.
Gardez à l' esprit que l' std
espace de noms a des tonnes d'identificateurs, dont beaucoup sont très plus communs (penser list
, sort
, string
, iterator
, etc.) qui sont très susceptibles d'apparaître dans tout autre code, aussi.
Si vous considérez cela improbable: il y avait une question posée ici sur Stack Overflow où cela s'est passé à peu près exactement (fonction incorrecte appelée en raison d'un std::
préfixe omis ) environ six mois après avoir donné cette réponse. Voici un autre exemple plus récent d'une telle question. C'est donc un vrai problème.
Voici un autre point de données: il y a de nombreuses années, je trouvais aussi ennuyeux de tout préfixer de la bibliothèque standard avec std::
. Ensuite, j'ai travaillé dans un projet où il a été décidé au départ que les using
directives et les déclarations sont interdites sauf pour les portées de fonction. Devinez quoi? Il a fallu à la plupart d'entre nous très peu de semaines pour s'habituer à écrire le préfixe, et après quelques semaines de plus, la plupart d'entre nous ont même convenu que cela rendait le code plus lisible . Il y a une raison à cela: que vous aimiez une prose plus courte ou plus longue est subjectif, mais les préfixes ajoutent objectivement de la clarté au code. Non seulement le compilateur, mais vous aussi, trouvez plus facile de voir à quel identificateur il fait référence.
En une décennie, ce projet est devenu plusieurs millions de lignes de code. Étant donné que ces discussions reviennent encore et encore, j'ai été une fois curieux de savoir à quelle fréquence la portée de la fonction (autorisée) using
était réellement utilisée dans le projet. J'en ai vérifié les sources et n'ai trouvé qu'une ou deux douzaines d'endroits où il était utilisé. Pour moi, cela indique qu'une fois essayé, les développeurs ne trouvent pas std::
assez pénible d'employer des directives en utilisant même une fois tous les 100 kLoC, même là où il était permis d'être utilisé.
Conclusion: le préfixe explicite de tout ne fait aucun mal, prend très peu de temps pour s'y habituer et présente des avantages objectifs. En particulier, cela rend le code plus facile à interpréter par le compilateur et par les lecteurs humains - et cela devrait probablement être l'objectif principal lors de l'écriture de code.
Le problème avec la mise using namespace
dans les fichiers d'en-tête de vos classes est que cela oblige quiconque veut utiliser vos classes (en incluant vos fichiers d'en-tête) à également «utiliser» (c'est-à-dire tout voir dans) ces autres espaces de noms.
Cependant, vous pouvez vous sentir libre de mettre une instruction using dans vos fichiers (privés) * .cpp.
Méfiez-vous que certaines personnes ne sont pas d'accord avec ce que je dis "n'hésitez pas" comme ça - parce que même si une using
déclaration dans un fichier cpp est meilleure que dans un en-tête (car cela n'affecte pas les personnes qui incluent votre fichier d'en-tête), elles pensent que ce n'est toujours pas le cas bon (car en fonction du code cela pourrait rendre l'implémentation de la classe plus difficile à maintenir). Cette entrée C ++ Super-FAQ dit,
La directive using existe pour le code C ++ hérité et pour faciliter la transition vers les espaces de noms, mais vous ne devriez probablement pas l'utiliser régulièrement, du moins pas dans votre nouveau code C ++.
La FAQ propose deux alternatives:
Une déclaration d'utilisation:
using std::cout; // a using-declaration lets you use cout without qualification cout << "Values:";
Tapez simplement std ::
std::cout << "Values:";
J'ai récemment rencontré une plainte concernant Visual Studio 2010 . Il s'est avéré que pratiquement tous les fichiers source avaient ces deux lignes:
using namespace std;
using namespace boost;
De nombreuses fonctionnalités Boost entrent dans la norme C ++ 0x, et Visual Studio 2010 a beaucoup de fonctionnalités C ++ 0x, donc soudainement, ces programmes ne se compilaient pas.
Par conséquent, éviter using namespace X;
est une forme de pérennité, un moyen de s'assurer qu'une modification des bibliothèques et / ou des fichiers d'en-tête utilisés ne va pas casser un programme.
Version courte: n'utilisez pas de using
déclarations globales ou de directives dans les fichiers d'en-tête. N'hésitez pas à les utiliser dans les fichiers d'implémentation. Voici ce que Herb Sutter et Andrei Alexandrescu ont à dire à propos de ce problème dans les normes de codage C ++ (c'est moi qui met l'accent sur les caractères gras):
Résumé
Les utilisations des espaces de noms sont pour votre commodité, pas pour vous d'infliger aux autres: n'écrivez jamais une déclaration using ou une directive using avant une directive #include.
Corollaire: dans les fichiers d'en-tête, n'écrivez pas au niveau de l'espace de noms en utilisant des directives ou en utilisant des déclarations; à la place, qualifiez explicitement tous les noms. (La deuxième règle découle de la première, car les en-têtes ne peuvent jamais savoir quels autres en-têtes #includes pourraient apparaître après eux.)
Discussion
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 directives #include 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. Ce sont plutôt eux qui rendent les espaces de noms utilisables .
Il ne faut pas utiliser la using
directive au niveau global, en particulier dans les en-têtes. Cependant, il existe des situations où cela est approprié même dans un fichier d'en-tête:
template <typename FloatType> inline
FloatType compute_something(FloatType x)
{
using namespace std; // No problem since scope is limited
return exp(x) * (sin(x) - cos(x * 2) + sin(x * 3) - cos(x * 4));
}
C'est mieux que la qualification explicite ( std::sin
, std::cos
...), car elle est plus courte et a la capacité de travailler avec des types à virgule flottante définis par l'utilisateur (via la recherche dépendante de l'argument (ADL)).
Ne l'utilisez pas globalement
Il est considéré comme «mauvais» uniquement lorsqu'il est utilisé globalement . Parce que:
- Vous encombrez l'espace de noms dans lequel vous programmez.
- Les lecteurs auront du mal à voir d'où vient un identifiant particulier, lorsque vous en utilisez plusieurs
using namespace xyz
. - Ce qui est vrai pour les autres lecteurs de votre code source l'est encore plus pour le lecteur le plus fréquent: vous-même. Revenez dans un an ou deux et jetez un œil ...
- Si vous ne parlez que de cela,
using namespace std
vous n'êtes peut-être pas au courant de tout ce que vous récupérez - et lorsque vous en ajoutez une autre#include
ou que vous passez à une nouvelle révision C ++, vous pourriez avoir des conflits de noms dont vous n'étiez pas au courant.
Vous pouvez l'utiliser localement
Allez-y et utilisez-le localement (presque) librement. Ceci, bien sûr, vous empêche de répéter std::
- et la répétition est également mauvaise.
Un idiome pour l'utiliser localement
En C ++ 03, il y avait un idiome - code standard - pour implémenter une swap
fonction pour vos classes. Il a été suggéré que vous utilisiez réellement un local using namespace std
- ou du moins using std::swap
:
class Thing {
int value_;
Child child_;
public:
// ...
friend void swap(Thing &a, Thing &b);
};
void swap(Thing &a, Thing &b) {
using namespace std; // make `std::swap` available
// swap all members
swap(a.value_, b.value_); // `std::stwap(int, int)`
swap(a.child_, b.child_); // `swap(Child&,Child&)` or `std::swap(...)`
}
Cela fait la magie suivante:
- Le compilateur choisira le
std::swap
pourvalue_
, c'est-à-direvoid std::swap(int, int)
. - Si vous avez une surcharge
void swap(Child&, Child&)
implémentée, le compilateur la choisira. - Si vous n'avez pas cette surcharge, le compilateur les utilisera
void std::swap(Child&,Child&)
et fera de son mieux en les échangeant.
Avec C ++ 11, il n'y a plus aucune raison d'utiliser ce modèle. L'implémentation de a std::swap
été modifiée pour trouver une surcharge potentielle et la choisir.
Si vous importez les fichiers d' en- tête de droite , vous avez soudainement des noms comme hex, left, plusou countdans votre portée globale. Cela peut être surprenant si vous ne savez pas qui std::
contient ces noms. Si vous essayez également d'utiliser ces noms localement, cela peut entraîner une certaine confusion.
Si tout le contenu standard est dans son propre espace de noms, vous n'avez pas à vous soucier des collisions de noms avec votre code ou d'autres bibliothèques.
Une autre raison est la surprise.
Si je vois cout << blah
, au lieu de std::cout << blah
penser: qu'est-ce que c'est cout
? Est-ce normal cout
? Est-ce quelque chose de spécial?
Les programmeurs expérimentés utilisent tout ce qui résout leurs problèmes et évitent ce qui crée de nouveaux problèmes, et ils évitent les directives d'utilisation au niveau du fichier d'en-tête pour cette raison exacte.
Les programmeurs expérimentés essaient également d'éviter la qualification complète des noms dans leurs fichiers source. Une raison mineure à cela est qu'il n'est pas élégant d'écrire plus de code alors que moins de code suffit à moins qu'il y ait de bonnes raisons . Une des principales raisons à cela est la désactivation de la recherche dépendante des arguments (ADL).
Quelles sont ces bonnes raisons ? Parfois, les programmeurs veulent explicitement désactiver l'ADL, d'autres fois ils veulent lever l'ambiguïté.
Donc, ce qui suit est OK:
- Directives d'utilisation au niveau de la fonction et déclarations d'utilisation dans les implémentations de fonctions
- Déclarations d'utilisation au niveau du fichier source dans les fichiers source
- (Parfois) directives using au niveau du fichier source
Je suis d'accord qu'il ne devrait pas être utilisé globalement, mais ce n'est pas si mal à utiliser localement, comme dans un namespace
. Voici un exemple tiré de «Le langage de programmation C ++» :
namespace My_lib {
using namespace His_lib; // Everything from His_lib
using namespace Her_lib; // Everything from Her_lib
using His_lib::String; // Resolve potential clash in favor of His_lib
using Her_lib::Vector; // Resolve potential clash in favor of Her_lib
}
Dans cet exemple, nous avons résolu les conflits de noms potentiels et les ambiguïtés résultant de leur composition.
Les noms qui y sont explicitement déclarés (y compris les noms déclarés par using-declarations like His_lib::String
) ont la priorité sur les noms rendus accessibles dans une autre portée par une using-directive ( using namespace Her_lib
).
Je considère également que c'est une mauvaise pratique. Pourquoi? Un jour, j'ai pensé que la fonction d'un espace de noms était de diviser les choses, donc je ne devrais pas le gâcher en jetant tout dans un seul sac global.
Cependant, si j'utilise souvent 'cout' et 'cin', j'écris: using std::cout; using std::cin;
dans le fichier .cpp (jamais dans le fichier d'en-tête avec lequel il se propage #include
). Je pense que personne sensé ne nommera jamais un flux cout
ou cin
. ;)
C'est agréable de voir le code et de savoir ce qu'il fait. Si je vois, std::cout
je sais que c'est le cout
flux de la std
bibliothèque. Si je vois, cout
je ne sais pas. Cela pourrait être le cout
flux de la std
bibliothèque. Ou il pourrait y avoir int cout = 0;
dix lignes plus haut dans la même fonction. Ou une static
variable nommée cout
dans ce fichier. Ça pourrait être n'importe quoi.
Maintenant, prenez une base de code d'un million de lignes, ce qui n'est pas particulièrement gros, et vous recherchez un bogue, ce qui signifie que vous savez qu'il y a une ligne dans ce million de lignes qui ne fait pas ce qu'elle est censée faire. cout << 1;
pouvait lire un static int
nom cout
, le décaler d'un bit vers la gauche et jeter le résultat. À la recherche d'un bug, je devrais le vérifier. Pouvez-vous voir comment je préfère vraiment voir std::cout
?
C'est une de ces choses qui semble une très bonne idée si vous êtes enseignant et que vous n'avez jamais eu à écrire et à maintenir un code pour vivre. J'adore voir du code où (1) je sais ce qu'il fait; et (2) je suis convaincu que la personne qui l'a écrit savait ce qu'elle faisait.
Tout est question de gestion de la complexité. L'utilisation de l'espace de noms attirera des choses que vous ne voulez pas et rendra peut-être plus difficile le débogage (je dis peut-être). Utiliser std :: partout est plus difficile à lire (plus de texte et tout ça).
Chevaux pour les cours - gérez votre complexité comme vous le pouvez et vous vous sentez le mieux.
Considérer
// myHeader.h
#include <sstream>
using namespace std;
// someoneElses.cpp/h
#include "myHeader.h"
class stringstream { // Uh oh
};
Notez que ceci est un exemple simple. Si vous avez des fichiers avec 20 inclusions et d'autres importations, vous aurez une tonne de dépendances à parcourir pour résoudre le problème. Le pire, c'est que vous pouvez obtenir des erreurs sans rapport avec d'autres modules en fonction des définitions qui sont en conflit.
Ce n'est pas horrible, mais vous vous épargnerez des maux de tête en ne l'utilisant pas dans les fichiers d'en-tête ou dans l'espace de noms global. C'est probablement bien de le faire dans des portées très limitées, mais je n'ai jamais eu de problème à taper les cinq caractères supplémentaires pour clarifier d'où viennent mes fonctions.
Un exemple concret pour clarifier la préoccupation. Imaginez que vous ayez une situation où vous avez deux bibliothèques foo
et bar
, chacune avec son propre espace de noms:
namespace foo {
void a(float) { /* Does something */ }
}
namespace bar {
...
}
Maintenant, disons que vous utilisez foo
et bar
ensemble dans votre propre programme comme suit:
using namespace foo;
using namespace bar;
void main() {
a(42);
}
À ce stade, tout va bien. Lorsque vous exécutez votre programme, il «fait quelque chose». Mais plus tard, vous mettez à jour bar
et disons que cela a changé pour ressembler à:
namespace bar {
void a(float) { /* Does something completely different */ }
}
À ce stade, vous obtiendrez une erreur de compilation:
using namespace foo;
using namespace bar;
void main() {
a(42); // error: call to 'a' is ambiguous, should be foo::a(42)
}
Vous aurez donc besoin de faire un peu de maintenance pour clarifier que «a» signifiait foo::a
. Ce n'est pas souhaitable, mais heureusement, c'est assez facile (il suffit d'ajouter foo::
devant tous les appels a
que le compilateur marque comme ambiguë).
Mais imaginez un scénario alternatif où la barre a changé à la place pour ressembler à ceci à la place:
namespace bar {
void a(int) { /* Does something completely different */ }
}
À ce stade, votre appel à a(42)
se lie soudainement au bar::a
lieu de foo::a
et au lieu de faire «quelque chose», il fait «quelque chose de complètement différent». Aucun avertissement du compilateur ou quoi que ce soit. Votre programme commence silencieusement à faire quelque chose de complètement différent qu'avant.
Lorsque vous utilisez un espace de noms, vous risquez un scénario comme celui-ci, c'est pourquoi les gens ne sont pas à l'aise avec les espaces de noms. Plus il y a d'éléments dans un espace de noms, plus le risque de conflit est grand, de sorte que les utilisateurs peuvent être encore plus mal à l'aise d'utiliser l'espace de noms std
(en raison du nombre d'éléments dans cet espace de noms) que d'autres espaces de noms.
En fin de compte, il s'agit d'un compromis entre l'écriture et la fiabilité / maintenabilité. La lisibilité peut également jouer un rôle, mais je pouvais voir des arguments en faveur de cela dans les deux sens. Normalement, je dirais que la fiabilité et la maintenabilité sont plus importantes, mais dans ce cas, vous paierez constamment le coût de l'écriture pour un impact assez rare de fiabilité / maintenabilité. Le «meilleur» compromis déterminera votre projet et vos priorités.
Utiliser plusieurs espaces de noms en même temps est évidemment une recette pour le désastre, mais utiliser JUST namespace std
et uniquement namespace std
n'est pas si grave à mon avis car la redéfinition ne peut se faire que par votre propre code ...
Alors considérez-les simplement comme des noms réservés comme "int" ou "class" et c'est tout.
Les gens devraient cesser d'être si anaux à ce sujet. Votre professeur avait raison depuis le début. Utilisez juste UN espace de noms; c'est tout l'intérêt d'utiliser les espaces de noms en premier lieu. Vous n'êtes pas censé en utiliser plus d'un à la fois. À moins que ce ne soit le vôtre. Encore une fois, la redéfinition ne se produira pas.
Vous devez être capable de lire du code écrit par des personnes qui ont des opinions différentes de vous sur le style et les meilleures pratiques.
Si vous n'utilisez que
cout
, personne n'est confus. Mais quand vous avez beaucoup d'espaces de noms qui volent et que vous voyez cette classe et que vous ne savez pas exactement ce qu'elle fait, avoir l'espace de noms explicite agit comme une sorte de commentaire. Vous pouvez voir à première vue, "oh, c'est une opération de système de fichiers" ou "qui fait des trucs réseau".
Je suis d'accord avec les autres ici, mais je voudrais aborder les préoccupations concernant la lisibilité - vous pouvez éviter tout cela en utilisant simplement des typedefs en haut de votre fichier, fonction ou déclaration de classe.
Je l'utilise généralement dans ma déclaration de classe car les méthodes d'une classe ont tendance à traiter des types de données similaires (les membres) et un typedef est une opportunité d'attribuer un nom significatif dans le contexte de la classe. Cela facilite en fait la lisibilité des définitions des méthodes de classe.
// Header
class File
{
typedef std::vector<std::string> Lines;
Lines ReadLines();
}
et dans la mise en œuvre:
// .cpp
Lines File::ReadLines()
{
Lines lines;
// Get them...
return lines;
}
par opposition à:
// .cpp
vector<string> File::ReadLines()
{
vector<string> lines;
// Get them...
return lines;
}
ou:
// .cpp
std::vector<std::string> File::ReadLines()
{
std::vector<std::string> lines;
// Get them...
return lines;
}
Un espace de noms est une portée nommée. Les espaces de noms sont utilisés pour regrouper les déclarations associées et pour séparer les éléments séparés. Par exemple, deux bibliothèques développées séparément peuvent utiliser le même nom pour faire référence à des éléments différents, mais un utilisateur peut toujours utiliser les deux:
namespace Mylib{
template<class T> class Stack{ /* ... */ };
// ...
}
namespace Yourlib{
class Stack{ /* ... */ };
// ...
}
void f(int max) {
Mylib::Stack<int> s1(max); // Use my stack
Yourlib::Stack s2(max); // Use your stack
// ...
}
La répétition d'un nom d'espace de noms peut être une distraction pour les lecteurs et les rédacteurs. Par conséquent, il est possible de déclarer que les noms d'un espace de noms particulier sont disponibles sans qualification explicite. Par exemple:
void f(int max) {
using namespace Mylib; // Make names from Mylib accessible
Stack<int> s1(max); // Use my stack
Yourlib::Stack s2(max); // Use your stack
// ...
}
Les espaces de noms fournissent un outil puissant pour la gestion de différentes bibliothèques et de différentes versions de code. En particulier, ils offrent aux programmeurs des alternatives expliquant comment faire référence à un nom non local.
Source: Un aperçu du langage de programmation C ++ par Bjarne Stroustrup
Un exemple où using namespace std
lève une erreur de compilation en raison de l'ambiguïté de count, qui est également une fonction dans la bibliothèque d'algorithmes.
#include <iostream>
#include <algorithm>
using namespace std;
int count = 1;
int main() {
cout << count << endl;
}
Cela n'empire pas les performances de votre logiciel ou de votre projet. L'inclusion de l'espace de noms au début de votre code source n'est pas mauvaise. L'inclusion de l' using namespace std
instruction varie en fonction de vos besoins et de la façon dont vous développez le logiciel ou le projet.
Le namespace std
contient les fonctions et variables standard C ++. Cet espace de noms est utile lorsque vous utilisez souvent les fonctions standard C ++.
Comme mentionné dans cette page :
L'instruction utilisant l'espace de noms std est généralement considérée comme une mauvaise pratique. L'alternative à cette instruction est de spécifier l'espace de noms auquel appartient l'identifiant à l'aide de l'opérateur scope (: :) à chaque fois que nous déclarons un type.
Et voyez cet avis :
Il n'y a aucun problème à utiliser "using namespace std" dans votre fichier source lorsque vous faites un usage intensif de l'espace de noms et que vous savez avec certitude que rien ne va entrer en collision.
Certaines personnes avaient dit que c'était une mauvaise pratique d'inclure le using namespace std
dans vos fichiers source parce que vous invoquez à partir de cet espace de noms toutes les fonctions et variables. Lorsque vous souhaitez définir une nouvelle fonction avec le même nom qu'une autre fonction contenue dans le, namespace std
vous surchargez la fonction et cela peut produire des problèmes dus à la compilation ou à l'exécution. Il ne se compilera pas ou ne s'exécutera pas comme prévu.
Comme mentionné dans cette page :
Bien que l'instruction nous évite de taper std :: chaque fois que nous souhaitons accéder à une classe ou à un type défini dans l'espace de noms std, elle importe l'intégralité de l'espace de noms std dans l'espace de noms actuel du programme. Prenons quelques exemples pour comprendre pourquoi ce n'est peut-être pas une si bonne chose
...
Maintenant, à un stade ultérieur de développement, nous souhaitons utiliser une autre version de cout qui est implémentée de manière personnalisée dans une bibliothèque appelée «foo» (par exemple)
...
Remarquez comment il y a une ambiguïté, vers quelle bibliothèque le cout indique-t-il? Le compilateur peut détecter cela et ne pas compiler le programme. Dans le pire des cas, le programme peut toujours compiler mais appeler la mauvaise fonction, car nous n'avons jamais spécifié à quel espace de noms l'identifiant appartenait.
Je ne pense pas que ce soit forcément une mauvaise pratique dans toutes les conditions, mais il faut être prudent quand on l'utilise. Si vous écrivez une bibliothèque, vous devriez probablement utiliser les opérateurs de résolution de portée avec l'espace de noms pour empêcher votre bibliothèque de se heurter à d'autres bibliothèques. Pour le code au niveau de l'application, je ne vois rien de mal à cela.
Je suis d'accord avec les autres - cela demande des conflits de noms, des ambiguïtés et le fait est que c'est moins explicite. Bien que je puisse voir l'utilisation de using
, ma préférence personnelle est de la limiter. Je considérerais également fortement ce que certains autres ont souligné:
Si vous voulez trouver un nom de fonction qui pourrait être un nom assez courant, mais que vous ne voulez le trouver que dans l' std
espace de noms (ou l'inverse - vous voulez changer tous les appels qui ne sont pas dans l'espace de noms std
, l'espace de noms X
, ...), alors comment proposez-vous de faire cela?
Vous pourriez écrire un programme pour le faire, mais ne serait-il pas préférable de passer du temps à travailler sur votre projet lui-même plutôt que d'écrire un programme pour maintenir votre projet?
Personnellement, le std::
préfixe ne me dérange pas vraiment . J'aime le look plus que de ne pas l'avoir. Je ne sais pas si c'est parce que c'est explicite et me dit "ce n'est pas mon code ... j'utilise la bibliothèque standard" ou si c'est autre chose, mais je pense que ça a l'air plus joli. Cela peut être étrange étant donné que je ne suis entré dans C ++ que récemment (utilisé et utilise toujours C et d'autres langages pendant beaucoup plus longtemps et C est mon langage préféré de tous les temps, juste au-dessus de l'assembly).
Il y a une autre chose bien qu'elle soit quelque peu liée à ce qui précède et à ce que d'autres soulignent. Bien que cela puisse être une mauvaise pratique, je réserve parfois std::name
la version standard de la bibliothèque et le nom pour l'implémentation spécifique au programme. Oui, en effet, cela pourrait vous mordre et vous mordre fort, mais tout se résume à ce que j'ai commencé ce projet à partir de zéro, et je suis le seul programmeur pour cela. Exemple: je surcharge std::string
et l'appelle string
. J'ai des ajouts utiles. Je l'ai fait en partie à cause de ma tendance C et Unix (+ Linux) vers les noms minuscules.
En plus de cela, vous pouvez avoir des alias d'espace de noms. Voici un exemple où il est utile qui n'a peut-être pas été mentionné. J'utilise le standard C ++ 11 et spécifiquement avec libstdc ++. Eh bien, il n'a pas de std::regex
support complet . Bien sûr, il compile, mais il lève une exception en ce sens qu'il s'agit d'une erreur de la part du programmeur. Mais c'est un manque de mise en œuvre.
Alors, voici comment je l'ai résolu. Installez le regex de Boost et liez-le. Ensuite, je fais ce qui suit pour que lorsque libstdc ++ l'ait entièrement implémenté, je n'ai qu'à supprimer ce bloc et le code reste le même:
namespace std
{
using boost::regex;
using boost::regex_error;
using boost::regex_replace;
using boost::regex_search;
using boost::regex_match;
using boost::smatch;
namespace regex_constants = boost::regex_constants;
}
Je ne discuterai pas de la question de savoir si c'est une mauvaise idée ou non. Je dirai cependant qu'il le maintient propre pour mon projet et en même temps le rend spécifique: c'est vrai, je dois utiliser Boost, mais je l'utilise comme libstdc ++ l'aura éventuellement. Oui, démarrer votre propre projet et commencer avec une norme (...) au tout début va très loin en aidant à la maintenance, au développement et à tout ce qui est impliqué dans le projet!
Juste pour clarifier quelque chose: je ne pense pas que ce soit une bonne idée d'utiliser un nom de classe / quoi que ce soit dans la STL délibérément et plus spécifiquement à la place de. La chaîne est l'exception (ignorez le premier, ci-dessus ou le deuxième ici, jeu de mots si vous devez le faire) pour moi car je n'aimais pas l'idée de «String».
Dans l'état actuel des choses, je suis toujours très biaisé envers C et contre C ++. En épargnant les détails, une grande partie de ce sur quoi je travaille s'adapte davantage à C (mais c'était un bon exercice et un bon moyen de me faire a. Apprendre une autre langue et b. Essayer de ne pas être moins biaisé contre les objets / classes / etc, ce qui est peut-être mieux dit comme moins fermé d'esprit, moins arrogant et plus tolérant.). Mais ce qui est utile, c'est ce que certains ont déjà suggéré: j'utilise effectivement list (c'est assez générique, n'est-ce pas?), Et trie (même chose) pour en nommer deux qui provoqueraient un conflit de nom si je devais faire using namespace std;
, et donc à cette fin, je préfère être précis, maîtriser et savoir que si je veux que ce soit l'usage standard, je devrai le spécifier. En termes simples: aucune hypothèse n'est autorisée.
Et quant à l'intégration de l'expression régulière de Boost std
. Je fais cela pour une intégration future et - encore une fois, j'admets tout à fait que c'est un parti pris - je ne pense pas que ce soit aussi laid que boost::regex:: ...
. En effet, c'est autre chose pour moi. Il y a beaucoup de choses en C ++ que je n'ai pas encore entièrement acceptées dans les regards et les méthodes (un autre exemple: les modèles variadiques contre les arguments var [bien que j'admets que les modèles variadiques sont très très utiles!]). Même ceux que j'accepte, c'était difficile, et j'ai toujours des problèmes avec eux.
D'après mon expérience, si vous avez plusieurs bibliothèques qui utilisent par exemple cout
, mais dans un but différent, vous pouvez utiliser le mauvais cout
.
Par exemple, si je tape, using namespace std;
et using namespace otherlib;
et que je tape juste cout
(ce qui se trouve être dans les deux), plutôt que std::cout
(ou 'otherlib::cout'
), vous pourriez utiliser le mauvais et obtenir des erreurs. Son utilisation est beaucoup plus efficace et efficiente std::cout
.
C'est au cas par cas. Nous voulons minimiser le «coût total de possession» du logiciel sur sa durée de vie. Dire "utiliser l'espace de noms std" a des coûts, mais ne pas l' utiliser a aussi un coût en lisibilité.
Les gens soulignent à juste titre que lors de son utilisation, lorsque la bibliothèque standard introduit de nouveaux symboles et définitions, votre code cesse de se compiler et vous pouvez être obligé de renommer des variables. Et pourtant, c'est probablement une bonne chose à long terme, car les futurs responsables seront momentanément confus ou distraits si vous utilisez un mot-clé dans un but surprenant.
Vous ne voulez pas avoir un modèle appelé vecteur, par exemple, qui n'est pas le vecteur connu de tout le monde. Et le nombre de nouvelles définitions ainsi introduites dans la bibliothèque C ++ est suffisamment petit pour ne pas apparaître. Il y a un coût à faire ce type de changement, mais le coût n'est pas élevé et est compensé par la clarté acquise en n'utilisant pas les std
noms de symboles à d'autres fins.
Compte tenu du nombre de classes, de variables et de fonctions, indiquer std::
chacune d'elles pourrait augmenter votre code de 50% et vous rendre la tête plus difficile. Un algorithme ou une étape dans une méthode qui pourrait être effectuée sur un écran plein de code nécessite maintenant un défilement d'avant en arrière pour suivre. C'est un vrai coût. Ce n'est sans doute pas un coût élevé, mais les gens qui nient même son existence sont inexpérimentés, dogmatiques ou tout simplement faux.
J'offrirais les règles suivantes:
std
est différent de toutes les autres bibliothèques. C'est la bibliothèque que tout le monde a fondamentalement besoin de connaître, et à mon avis, il vaut mieux la considérer comme faisant partie de la langue. De manière générale, il existe un excellent cas,using namespace std
même s'il n'y en a pas pour d'autres bibliothèques.Ne forcez jamais la décision sur l'auteur d'une unité de compilation (un fichier .cpp) en le mettant
using
dans un en-tête. Reportez toujours la décision à l'auteur de l'unité de compilation. Même dans un projet qui a décidé de l'utiliserusing namespace std
partout, certains modules peuvent être traités comme des exceptions à cette règle.Même si la fonctionnalité d'espace de noms vous permet d'avoir de nombreux modules avec des symboles définis de la même manière, cela va être déroutant de le faire. Gardez les noms différents dans la mesure du possible. Même si vous n'utilisez pas la fonctionnalité d'espace de noms, si vous avez une classe nommée
foo
etstd
introduit une classe nomméefoo
, il est probablement préférable à long terme de renommer votre classe de toute façon.Une alternative à l'utilisation des espaces de noms consiste à utiliser manuellement les symboles d'espaces de noms en les préfixant. J'ai deux bibliothèques que j'utilise depuis des décennies, toutes deux commençant en fait par des bibliothèques C, où chaque symbole est préfixé par "AK" ou "SCWin". En général, c'est comme éviter la construction "using", mais vous n'écrivez pas les deux points.
AK::foo()
est à la placeAKFoo()
. Cela rend le code 5-10% plus dense et moins verbeux, et le seul inconvénient est que vous aurez de gros problèmes si vous devez utiliser deux bibliothèques de ce type qui ont le même préfixe. Notez que les bibliothèques X Window sont excellentes à cet égard, sauf qu'elles ont oublié de le faire avec quelques #defines: TRUE et FALSE auraient dû être XTRUE et XFALSE, et cela a créé un conflit d'espace de noms avec Sybase ou Oracle qui utilisaient également TRUE et FALSE avec des valeurs différentes! (ASCII 0 et 1 dans le cas de la base de données!) Un avantage particulier de ceci est qu'il s'applique parfaitement aux définitions de préprocesseur, alors que le C ++using
/namespace
système ne les gère pas. Un bel avantage de ceci est que cela donne une pente organique de faire partie d'un projet à finalement devenir une bibliothèque. Dans une de mes grandes applications, toutes les classes de fenêtres sont préfixéesWin
, tous les modules de traitement du signal Mod, et ainsi de suite. Il y a peu de chance que l'un de ces éléments soit réutilisé, il n'y a donc aucun avantage pratique à faire de chaque groupe une bibliothèque, mais cela rend évident en quelques secondes comment le projet se divise en sous-projets.
Avec les identifiants importés non qualifiés, vous avez besoin d'outils de recherche externes comme grep pour savoir où les identifiants sont déclarés. Cela rend le raisonnement sur l'exactitude du programme plus difficile.
Cela dépend de l'endroit où il se trouve. S'il s'agit d'un en-tête commun, vous diminuez la valeur de l'espace de noms en le fusionnant dans l'espace de noms global. Gardez à l'esprit que cela pourrait être une manière intéressante de créer des globaux de module.
Il s'agit d'une mauvaise pratique, souvent connue sous le nom de pollution globale des espaces de noms. Des problèmes peuvent survenir lorsque plusieurs espaces de noms ont le même nom de fonction avec signature, il sera alors ambigu pour le compilateur de décider lequel appeler et tout cela peut être évité lorsque vous spécifiez l'espace de noms avec votre appel de fonction comme std::cout
. J'espère que cela t'aides. :)
Pour répondre à votre question, je regarde les choses de cette façon pratiquement: beaucoup de programmeurs (pas tous) invoquent namespace std. Par conséquent, il faut avoir l'habitude de NE PAS utiliser de choses qui empiètent ou utilisent les mêmes noms que ce qui est dans l'espace de noms std. C’est beaucoup d’accord, mais pas tant par rapport au nombre de mots cohérents et de pseudonymes possibles que l’on peut trouver à proprement parler.
Je veux dire vraiment ... dire "ne comptez pas sur cette présence", c'est juste vous mettre en place pour que cela ne soit PAS présent. Vous allez constamment avoir des problèmes pour emprunter des extraits de code et les réparer constamment. Gardez simplement vos trucs définis par l'utilisateur et empruntés dans une portée limitée comme ils devraient l'être et soyez TRÈS économe avec les globaux (honnêtement, les globaux devraient presque toujours être un dernier recours aux fins de "compiler maintenant, de raison plus tard"). Vraiment, je pense que c'est un mauvais conseil de votre professeur car l'utilisation de std fonctionnera à la fois pour "cout" et "std :: cout" mais NE PAS utiliser std ne fonctionnera que pour "std :: cout". Vous n'aurez pas toujours la chance d'écrire tout votre propre code.
REMARQUE: ne vous concentrez pas trop sur les problèmes d'efficacité jusqu'à ce que vous en appreniez un peu plus sur le fonctionnement des compilateurs. Avec un peu d'expérience en codage, vous n'avez pas besoin d'en apprendre beaucoup sur eux avant de réaliser à quel point ils sont capables de généraliser un bon code en quelque chose de simple. Tout aussi simple que si vous aviez tout écrit en C. Un bon code n'est aussi complexe que nécessaire.