Почему мы не можем объявлять псевдонимы пространства имен внутри класса?

Похоже, что невозможно объявить псевдоним пространства имен внутри класса; однако мы можем сделать это на уровне функции (проверено с помощью g++ 4.3.4):

namespace A
{
}

class C
{
  namespace N = A; // error: expected unqualified-id before `namespace'
};

class D
{
  void f();
};

void D::f()
{
  namespace N = A; // OK
}

Любая идея, почему такое ограничение существует? Это не похоже на typedefs, которые могут быть объявлены внутри класса.

Ответы

Ответ 1

Я не эксперт по стандарту С++, но я выдержу свою шею и пойду, чтобы ответить на ваш вопрос. Я предполагаю, что использование namespace N = A в объявлении класса не соответствует определению того, как должен определяться член класса.

Стандарт С++ определяет член класса как

member-specification:
  member-declaration member-specification_opt
  access-specifier : member-specification_opt
member-declaration:
  decl-specifier-seq_opt member-declarator-list_opt ;
  function-definition ;opt
  ::opt nested-name-specifier templateopt unqualified-id ;
  using-declaration
  static_assert-declaration
  template-declaration
member-declarator-list:
  member-declarator
  member-declarator-list , member-declarator
member-declarator:
  declarator pure-specifier_opt
  declarator constant-initializer_opt
  identifier_opt : constant-expression
pure-specifier:
  = 0
constant-initializer:
  = constant-expression

Важным моментом, заключающимся в = в объявлении, компилятор ожидает либо инструкции pure-specifier, либо константы-инициализатора, а так как линия не заканчивается нулем, мы не применяем в этом чистом-спецификаторе случай.

Анализируя объявление namespace N = A, компилятор видит это как

declarator = constant-expression

А поскольку namespace является ключевым словом, его нельзя использовать.

typedef разрешен, потому что (из стандарта)

Вложенные типы - это классы и перечисления, определенные в классе, и произвольные типы, объявленные как члены, используя объявление typedef.

Ответ 2

Согласно стандарту С++ 3.3.6

Следующие правила описывают область имен, объявленных в классах.

1) Потенциальная область имени, объявленного в классе, состоит не только из декларативной области, следующей за объявлением имен, но также из всех тел функций, аргументов по умолчанию и конструкторов-инициализаторов конструктора в этом классе (включая такие вещи в вложенные классы)............

Таким образом, вы можете объявлять только вещи из этого списка в классе. Объявление чего-либо еще в области класса недействительно. Не только альянс пространства имен, но и пространство имен. Например

class myClass
{
    //compilation error !!!
    namespace myNamespace
    {
    }
    using namespace std;//another compilation error
}

Изменить:

Любая идея, почему такое ограничение существует? Это не похоже на typedefs, которые могут быть объявлены внутри класса.

Потому что использование typedefs в классах очень полезно (например, vector<int>::iterator), а для пространств имен - бесполезно. Рассмотрим следующий код

class myClass
{
   namespce N=std;
};

//now let use N
MyClass::N::vector<int> v;//don't you think, that this syntax is horrible, and useless?????

Для сравнения см., что он делает в функции

void f()
{
    namespace bnu= boost::numeric::ublas;
    bnu::matrix<int> m;//and now we can use short name bnu
}

Для класса мы можем объявить альянс namespace в файле cpp и НЕТ НЕОБХОДИМО, чтобы daclare его в объявлении класса.

Ответ 3

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

class myClass
{
   private:
        namespace TransAndRotIdx {
            enum { U, V, W, P, Q, R }; };
        namespace RotIdx {
            enum { P, Q, R }; };
        double tr[6];
        double r[3];

    public:
        double getTranslationMag(void)
        {
            using namespace TransAndRotIdx;
            return sqrt(tr[U]*tr[U]+tr[V]*tr[V]+tr[W]*tr[W]);
        }
        double getRotationMag(void)
        {
            using namespace RotIdx;
            return sqrt(tr[P]*tr[P]+tr[Q]*tr[Q]+tr[R]*tr[R]);
        }
}