web-dev-qa-db-fra.com

Pourquoi "utiliser l'espace de noms X;" n'est pas autorisé à l'intérieur du niveau classe / struct?

class C {
  using namespace std;  // error
};
namespace N {
  using namespace std; // ok
}
int main () {
  using namespace std; // ok
}

Edit: Vous voulez connaître la motivation derrière cela.

75
iammilind

Je ne sais pas exactement, mais je suppose que permettre cela à la portée de la classe pourrait causer de la confusion:

namespace Hello
{
    typedef int World;
}

class Blah
{
    using namespace Hello;
public:
    World DoSomething();
}

//Should this be just World or Hello::World ?
World Blah::DoSomething()
{
    //Is the using namespace valid in here?
}

Puisqu'il n'y a aucun moyen évident de le faire, la norme dit simplement que vous ne pouvez pas.

Maintenant, la raison pour laquelle cela est moins déroutant lorsque nous parlons de portées d'espace de noms:

namespace Hello
{
    typedef int World;
}

namespace Other
{
    using namespace Hello;
    World DoSomething();
}

//We are outside of any namespace, so we have to fully qualify everything. Therefore either of these are correct:

//Hello was imported into Other, so everything that was in Hello is also in Other. Therefore this is okay:
Other::World Other::DoSomething()
{
    //We're outside of a namespace; obviously the using namespace doesn't apply here.
    //EDIT: Apparently I was wrong about that... see comments. 
}

//The original type was Hello::World, so this is okay too.
Hello::World Other::DoSomething()
{
    //Ditto
}

namespace Other
{
    //namespace Hello has been imported into Other, and we are inside Other, so therefore we never need to qualify anything from Hello.
    //Therefore this is unambiguiously right
    World DoSomething()
    {
        //We're inside the namespace, obviously the using namespace does apply here.
    }
}
30
Billy ONeal

Parce que la norme C++ l'interdit explicitement. Depuis C++ 03 §7.3.4 [namespace.udir]:

directive d'utilisation: 
 en utilisant l'espace de noms ::optspécificateur de nom imbriquéoptnamespace-name ; 

A sing-directive ne doit pas apparaître dans la portée de la classe, mais peut apparaître dans la portée de l'espace de noms ou dans la portée du bloc. [Remarque: lors de la recherche d'un nom d'espace de noms dans une directive using, seuls les noms d'espaces de noms sont pris en compte, voir 3.4.6. ]

Pourquoi la norme C++ l'interdit-elle? Je ne sais pas, demandez à un membre du comité ISO qui a approuvé la norme linguistique.

16
Adam Rosenfield

Je pense que la justification est que ce serait probablement déroutant. Actuellement, lors du traitement d'un identifiant de niveau de classe, la recherche cherchera d'abord dans la portée de la classe, puis dans l'espace de noms englobant. Autoriser le using namespace au niveau de la classe aurait quelques effets secondaires sur la façon dont la recherche est maintenant effectuée. En particulier, il devrait être effectué entre la vérification de cette portée de classe particulière et la vérification de l'espace de noms englobant. C'est-à-dire: 1) fusionner le niveau de classe et les recherches de niveau d'espace de nom utilisées, 2) rechercher l'espace de nom utilisé après l'étendue de classe mais avant toute autre étendue de classe, 3) recherchez l'espace de noms utilisé juste avant l'espace de noms englobant. 4) recherche fusionnée avec l'espace de noms englobant.

  1. Cela ferait une grande différence, où un identifiant au niveau de la classe occulterait tout identifiant dans l'espace de noms englobant, mais ce ne serait pas le cas shadow a utilisé espace de noms. L'effet serait étrange, dans la mesure où l'accès à l'espace de noms utilisé à partir d'une classe dans un espace de noms différent et du même espace de noms serait différent:

.

namespace A {
   void foo() {}
   struct B {
      struct foo {};
      void f() {
         foo();      // value initialize a A::B::foo object (current behavior)
      }
   };
}
struct C {
   using namespace A;
   struct foo {};
   void f() {
      foo();         // call A::foo
   }
};
  1. Recherchez juste après cette portée de classe. Cela aurait l'effet étrange d'occulter les membres des classes de base. La recherche actuelle ne mélange pas les recherches au niveau de la classe et de l'espace de noms, et lors de l'exécution de la recherche de classe, elle ira jusqu'aux classes de base avant en considérant l'espace de noms englobant . Le comportement serait surprenant en ce qu'il ne considérerait pas l'espace de noms à un niveau similaire à l'espace de noms englobant. Encore une fois, l'espace de noms utilisé serait priorisé sur l'espace de noms englobant.

.

namespace A {
   void foo() {}
}
void bar() {}
struct base {
   void foo();
   void bar();
};
struct test : base {
   using namespace A;
   void f() {
      foo();           // A::foo()
      bar();           // base::bar()
   }
};
  1. Recherchez juste avant l'espace de noms englobant. Le problème avec cette approche est encore une fois qu'elle serait surprenante pour beaucoup. Considérez que l'espace de noms est défini dans une unité de traduction différente, de sorte que le code suivant ne peut pas être vu en même temps:

.

namespace A {
   void foo( int ) { std::cout << "int"; }
}
void foo( double ) { std::cout << "double"; }
struct test {
   using namespace A;
   void f() {
      foo( 5.0 );          // would print "int" if A is checked *before* the
                           // enclosing namespace
   }
};
  1. Fusionnez avec l'espace de noms englobant. Cela aurait exactement le même effet que l'application de la déclaration using au niveau de l'espace de noms. Cela n'ajouterait aucune nouvelle valeur à cela, mais compliquerait en revanche la recherche d'implémenteurs de compilateur. La recherche d'identifiant d'espace de noms est désormais indépendante de l'endroit où dans le code la recherche est déclenchée. À l'intérieur d'une classe, si la recherche ne trouve pas l'identifiant à la portée de la classe, il reviendra à la recherche d'espace de noms, mais c'est exactement la même recherche d'espace de noms que celle utilisée dans une définition de fonction, il n'est pas nécessaire de maintenir un nouvel état. Lorsque la déclaration using est trouvée au niveau de l'espace de noms, le contenu de l'espace de noms utilisé est amené dans cet espace de noms pour toutes les recherches impliquant l'espace de noms. Si using namespace était autorisé au niveau de la classe, il y aurait des résultats différents pour la recherche d'espace de noms du même espace de noms selon l'endroit d'où la recherche a été déclenchée, ce qui rendrait l'implémentation de la recherche beaucoup plus complexe sans valeur supplémentaire.

Quoi qu'il en soit, ma recommandation est pas d'employer le using namespace déclaration du tout. Il rend le code plus simple à raisonner sans avoir à garder à l'esprit le contenu de tous les espaces de noms.