web-dev-qa-db-fra.com

portée de l'utilisation de la déclaration dans un espace de noms

Est-il sûr (et correct) dans un fichier d'en-tête C++ d'utiliser la déclaration using dans un espace de noms comme suit:

#include <boost/numeric/ublas/vector.hpp>
namespace MyNamespace {
    using boost::numeric::ublas::vector;
    vector MyFunc(vector in);
}

C'est à dire. le "using boost :: numeric :: ublas :: vector" est-il correctement contenu dans le bloc MyNamespace, ou cela polluera-t-il l'espace de noms de tout fichier qui inclut cet en-tête?

45
Brett Ryland

Non, ce n'est pas sûr - cela ne polluera pas un autre espace de noms, mais c'est dangereux pour d'autres raisons:

Une directive using importera tout qui est actuellement visible par le nom que vous spécifiez dans l'espace de noms où vous l'utilisez. Alors que votre using ne sera visible que pour les utilisateurs de MyNamespace, d'autres choses de "l'extérieur" seront visibles pour votre déclaration using.

Alors, comment est-ce dangereux lorsqu'il est utilisé dans un en-tête? Puisqu'il importera des éléments visibles au point de la déclaration, le comportement exact dépendra de l'ordre des en-têtes que vous incluez avant la déclaration (il peut y avoir des éléments différents visibles de boost::numeric::ublas::vector). Puisque vous ne pouvez pas vraiment contrôler quels en-têtes sont inclus avant votre en-tête (et vous ne devriez pas l'être! Les en-têtes doivent être autosuffisants!), Cela peut entraîner des problèmes très étranges où votre fonction trouvera une chose dans une unité de compilation et une autre dans le suivant.

En règle générale, les déclarations using ne doivent être utilisées qu'après toutes les inclusions dans un fichier .cpp. Il y a aussi un article sur cette question exacte dans le livre "C++ Coding Standards" de Sutter et Alexandrescu (article 59). Voici une citation: "Mais voici le piège commun: Beaucoup de gens pensent que l'utilisation de déclarations émises au niveau de l'espace de noms (...) est sûre. Elles ne le sont pas. Elles sont au moins aussi dangereuses, et d'une manière plus subtile et plus insidieuse."

Même s'il est peu probable que le nom que vous êtes using n'existe nulle part ailleurs (comme c'est probablement le cas ici), les choses peuvent mal tourner: dans un en-tête, toutes les déclarations doivent être entièrement qualifié. C'est de la douleur, mais sinon, des choses étranges peuvent se produire.

Voir aussi Migration vers des espaces de noms , sing-declarations et namespace aliases et Namespace Naming pour des exemples et le problème décrit en détail.

39
ltjax

Une déclaration d'utilisation est, comme son nom l'indique, une déclaration. Toutes les déclarations sont étendues au bloc englobant (7.2), dans ce cas l'espace de noms MyNamespace. Il ne sera pas visible en dehors de cet espace de noms.

12
Björn Pollex

Il est sûr, mais il polluera l'espace de noms MyNamespace. Ainsi, tout fichier qui inclut cet en-tête aura des fonctions/classes dans le MyNamespace.

4
BЈовић

Pour résumer, non , les déclarations d'utilisation dans un en-tête ne sont pas pas correctes , même dans un espace de noms, pour 2 raisons. De plus, les déclarations d'utilisation dans un espace de noms dans un non-en-tête sont sujettes à erreur ou inutiles (voir fin). Les déclarations d'utilisation dans un en-tête ne sont pas correctes car:

  1. Ils introduisent un nom dans l'espace de noms, ce qui affecte tous les fichiers qui incluent l'en-tête.
  2. Ils n'introduisent que des déclarations pour le nom qui ont déjà été vues, ce qui signifie que le comportement dépend de l'ordre des inclusions !

Dans votre exemple, cela signifie que:

  1. Dans MyNamespace, vector peut maintenant devenir boost::numeric::ublas::vector, pour tous les fichiers qui incluent cet en-tête: il "pollue" l'espace de noms MyNamespace.
  2. Lequel boost::numeric::ublas::vector les déclarations importées dépendent des déclarations qui apparaissent avant cette déclaration d'utilisation, qui dépend de l'ordre des inclusions dans le fichier qui comprend cet en-tête, et de toutes son inclut (correctement, l'ordre des déclarations dans l'unité de traduction, après prétraitement).

Per votre commentaire du 30 mai 11 à 11:51 vous voulez réellement le comportement 1, mais cela ne fonctionne pas, en raison du problème 2. Vous pouvez obtenir le comportement souhaité en ayant un en-tête séparé qui est inclus après tous les autres (et qualifiant pleinement le nom dans d'autres en-têtes). Cependant, cela est fragile et donc déconseillé, de préférence réservé uniquement lors de la transition vers des espaces de noms:

//--- file myheader.hpp ---
#include <boost/numeric/ublas/vector.hpp>
namespace MyNamespace {
    ::boost::numeric::ublas::vector MyFunc(::boost::numeric::ublas::vector in);
}

//--- file myproject_last.hpp ---
namespace MyNamespace {
    using ::boost::numeric::ublas::vector;
}

//--- file myproject.cpp ---
#include "myheader.hpp"
// ...other includes
#include "myproject_last.hpp"

Voir GotW # 53: Migration vers des espaces de noms pour plus de détails, cette solution de contournement et des conseils: "L'espace de noms utilisant des déclarations ne doit jamais apparaître dans les fichiers d'en-tête."

Il est possible d'éviter le problème 1 en ajoutant un espace de noms sans nom autour de la déclaration using (pour éviter que ces noms ne soient visibles), puis un autre en dehors de l'espace de noms sans nom (pour rendre le nom souhaité lui-même visible), mais qui souffre toujours du problème 2 et affaiblit l'en-tête:

//--- file myheader.hpp ---
#include <boost/numeric/ublas/vector.hpp>
namespace MyNamespace {
    namespace {
        using ::boost::numeric::ublas::vector;
        vector MyFunc(vector in);
    }
    using MyFunc; // MyNamespace::(unique)::MyFunc > MyNamespace::MyFunc
}

En raison de ces problèmes, vous ne devez utiliser que des déclarations using dans des fichiers sans en-tête (.cc/.cpp): cela n'affecte pas les autres fichiers, donc le problème 1 est évité; et tous les en-têtes ont été inclus, donc le problème 2 est évité. Dans ce cas, c'est une question de goût que vous les placiez dans un espace de noms ou non, car ils n'affectent pas les autres fichiers; il est plus sûr de toujours utiliser des noms complets dans la déclaration using elle-même (absolue, en commençant par ::).

Le plus simple est de mettre toutes les déclarations d'utilisation en haut du fichier, après les inclusions, mais en dehors de tout espace de noms: c'est sûr, sans ambiguïté, facile à lire et permet aux noms d'être utilisés dans tout le fichier. Quelques écarts courants:

  1. Using-declaration dans une fonction (ou struct ou classe ou bloc imbriqué): fine . Cela minimise la portée et n'est qu'une question de goût: l'utilisation de la déclaration est proche de l'utilisation (gain de lisibilité), mais ils sont maintenant dispersés dans le fichier (perte de lisibilité).
  2. Using-declaration avec un nom relatif dans un espace de noms (nommé): sujet aux erreurs . Ceci est plus concis et ajoute de la clarté (noms associés utilisés dans l'espace de noms auquel ils se rapportent), mais est potentiellement ambigu (tout comme les inclus avec les chemins relatifs), et est plus sûr à éviter:

    using ::foo::bar;
    namespace foo { ... }
    
    namespace foo {
        // Implicitly ::foo:bar, could be ::bar, or ::other::foo::bar.
        using bar;
    }
    
  3. Using-declaration avec un nom absolu dans un espace de noms nommé: inutile . Cela n'introduit le nom que dans l'espace de noms, mais vous ne devez pas vous en soucier, car vous ne devez pas inclure le fichier .cc/.cpp:

    namespace foo {
        using ::bar;
    }
    
  4. Using-declaration dans un espace de noms sans nom: inutile, légèrement dangereux . Par exemple, si vous avez une fonction dans un espace de noms sans nom, par exemple un détail d'implémentation, vous pouvez avoir une déclaration using pour son type de retour ou ses types de paramètres. Cela introduit le nom dans cet espace de noms (il ne peut donc pas être référencé à partir d'autres fichiers), mais encore une fois, vous ne devriez pas vous en soucier, car vous ne devez pas inclure le fichier .cc/.cpp (les espaces de noms sans nom sont spécialement conçus pour éviter le nom se heurte au moment du lien, ce qui n'est pas applicable ici: c'est juste un alias au moment de la compilation). Pire, cela introduit une ambiguïté si ce nom existe déjà!

2
Nils von Barth

Il ne polluera aucun autre espace de noms, mais il polluera certainement l'espace de noms MyNamespace.

1
Puppy