Почему "использование пространства имен X;" не допускается внутри уровня класса / структуры? - PullRequest
73 голосов
/ 13 июня 2011
class C {
  using namespace std;  // error
};
namespace N {
  using namespace std; // ok
}
int main () {
  using namespace std; // ok
}

Редактировать : Хотите узнать мотивы этого.

Ответы [ 3 ]

30 голосов
/ 13 июня 2011

Я точно не знаю, но я предполагаю, что допущение этого в области видимости может вызвать путаницу:

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?
}

Поскольку очевидного способа сделать это не существует, стандарт просто говорит, что вы можетеt.

Теперь, причина, по которой это менее запутанно, когда мы говорим о пространствах имен:

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.
    }
}
15 голосов
/ 13 июня 2011

Потому что стандарт C ++ явно запрещает это.Из C ++ 03 §7.3.4 [namespace.udir]:

<i>using-directive</i>:
    using namespace ::<sub>opt</sub> <i>nested-name-specifier</i><sub>opt</sub> <i>namespace-name</i> ;

A using-директива не должна появляться в области видимости класса, но может появляться в области пространства именили в объеме блока.[Примечание: при поиске имени пространства имен в директиве using учитываются только имена пространств имен, см. 3.4.6.]

Почему стандарт C ++ запрещает это?Я не знаю, спросите члена комитета ИСО, который утвердил языковой стандарт.

8 голосов
/ 13 июня 2011

Я считаю, что обоснование состоит в том, что это, вероятно, сбивает с толку.В настоящее время при обработке идентификатора уровня класса поиск сначала выполняет поиск в области класса, а затем во вложенном пространстве имен.Разрешение using namespace на уровне класса будет иметь некоторые побочные эффекты от того, как теперь выполняется поиск.В частности, это должно быть выполнено где-то между проверкой конкретной области класса и проверкой пространства имен.То есть: 1) объединить поиск уровня класса и используемых уровней пространства имен, 2) поиск используемого пространства имен после области видимости класса, но перед любой другой областью класса, 3) поиск используемого пространства имен непосредственно перед включающим пространством имен.4) поиск объединен с вмещающим пространством имен.

  1. Это имело бы большое значение, когда идентификатор на уровне класса shadow любой идентификатор в вмещающем пространстве имен, но это не будет тень a используется пространство имен.Эффект был бы странным, так как доступ к используемому пространству имен из класса в другом пространстве имен и из того же пространства имен будет отличаться:

.

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. Поиск сразу после этой области видимости.Это имело бы странный эффект слежки за членами базовых классов.Текущий поиск не смешивает поиск на уровне класса и пространства имен, и при выполнении поиска по классу он пойдет до базовых классов до с учетом окружающего пространства имен.Поведение будет удивительным в том смысле, что оно не будет рассматривать пространство имен на уровне, аналогичном вмещающему пространству имен.Снова, используемое пространство имен будет иметь приоритет над вмещающим пространством имен.

.

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. Поиск прямо перед вмещающим пространством имен.Проблема с этим подходом состоит в том, что это было бы удивительно для многих.Учтите, что пространство имен определено в другой единице перевода, поэтому следующий код не может быть виден сразу:

.

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. Объединить с включеннымПространство имен.Это будет иметь тот же эффект, что и применение объявления using на уровне пространства имен.Это не добавит к этому никакого нового значения, но, с другой стороны, усложнит поиск разработчиков компиляторов.Поиск идентификатора пространства имен теперь не зависит от того, где в коде выполняется поиск.Когда внутри класса поиск не находит идентификатор в области видимости класса, он возвращается к поиску пространства имен, но это точно такой же поиск пространства имен, который используется в определении функции, нет необходимости поддерживать новое состояние.Когда объявление using найдено на уровне пространства имен, содержимое используемого пространства имен заносит в это пространство имен для всех поисков, включающих пространство имен.Если бы using namespace было разрешено на уровне класса, результаты поиска в пространстве имен для одного и того же пространства имен были бы разными, в зависимости от того, откуда был запущен поиск, и это сделало бы реализацию поиска намного более сложной без дополнительного значения.

В любом случае, я рекомендую , а не , чтобы использовать декларацию using namespace вообще.Это упрощает работу с кодом без необходимости помнить содержимое всех пространств имен.

...