Почему "использование пространства имен X"; не допускается внутри уровня класса/структуры?

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

Изменить. Хотите узнать мотивацию.

Ответ 1

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

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

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

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

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

Ответ 2

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

using-directive:
    using namespace ::optnested-name-specifieroptnamespace-name ;

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

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

Ответ 3

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

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

.

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

.

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

.

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

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

Ответ 4

Но зачем вам это нужно? Просто поместите класс в отдельный файл и укажите пространства имен, которые вы хотите, в случае, если вы не хотите, чтобы другие классы достигли их.