Почему я не могу объявить класс в пространстве имен, используя двойные двоеточия? - PullRequest
154 голосов
/ 13 января 2010
class Namespace::Class;

Почему я должен это делать?:

namespace Namespace {
    class Class;
}

При использовании VC ++ 8.0 компилятор выдает:

ошибка C2653: «Пространство имен»: это не имя класса или пространства имен

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

Есть ли другой способ объявить вперед класс, определенный в некотором пространстве имен? Синтаксис выше выглядит так, будто я «заново открываю» пространство имен и расширяю его определение. Что если Class на самом деле не было определено в Namespace? Не приведет ли это к ошибке в какой-то момент?

Ответы [ 5 ]

187 голосов
/ 14 января 2010

Вы получаете правильные ответы, позвольте мне попробовать переформулировать:

class Namespace::Class;

Почему я должен это делать?

Вы должны сделать это, потому что термин Namespace::Class говорит компилятору:

... ОК, компилятор. Иди найди пространство имен с именем Namespace, а внутри которые ссылаются на класс с именем Class.

Но компилятор не знает, о чем вы говорите, потому что он не знает пространства имен с именем Namespace. Даже если бы существовало пространство имен с именем Namespace, например:

namespace Namespace
{
};

class Namespace::Class;

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

Таким образом, вы можете фактически объявить класс в пространстве имен. Просто сделайте это:

namespace Namespace
{
    class Class;
};
82 голосов
/ 13 января 2010

Потому что ты не можешь. В языке C ++ полные имена используются только для обозначения существующих (т.е. ранее объявленных) сущностей. Их нельзя использовать для введения новых сущностей.

А вы на самом деле"открываете" пространство имен для объявления новых объектов. Если класс Class позднее определен как член другого пространства имен - это совершенно другой класс, который не имеет ничего общего с тем, который вы объявили здесь.

Как только вы дойдете до определения предварительно объявленного класса, вам не нужно снова «открывать» пространство имен. Вы можете определить его в глобальном пространстве имен (или в любом пространстве имен, включающем ваш Namespace) как

class Namespace::Class {
  /* whatever */
};

Поскольку вы имеете в виду сущность, которая уже была объявлена ​​в пространстве имен Namespace, вы можете использовать полное имя Namespace::Class.

21 голосов
/ 14 января 2010

Полагаю, по той же причине вы не можете объявить вложенные пространства имен за один раз:

namespace Company::Communications::Sockets {
}

и вы должны сделать это:

namespace Company {
  namespace Communications {
    namespace Sockets {
    }
  }
}
5 голосов
/ 27 июля 2018

Непонятно, что представляет собой тип объявленной вперед переменной. Предварительная декларация class Namespace::Class; может означать

namespace Namespace {
  class Class;
}

или

class Namespace {
public:
  class Class;
};
0 голосов
/ 12 декабря 2017

Существует множество превосходных ответов об обосновании, запрещающем это. Я просто хочу предоставить скучную стандартную оговорку, которая специально запрещает это. Это верно для C ++ 17 (n4659).

Данный абзац имеет вид [class.name] / 2 :

Объявление, состоящее исключительно из идентификатора ключа класса ; это либо переименование названия в текущей области или форварде объявление идентификатора в качестве имени класса. Это вводит класс имя в текущую область.

Выше определено, что составляет предварительное объявление (или объявление класса). По сути, это должен быть один из class identifier;, struct identifier; или union identifier;, где identifier - это общее лексическое определение в [lex.name] :

identifier:
  identifier-nondigit
  identifier identifier-nondigit
  identifier digit
identifier-nondigit:
  nondigit
  universal-character-name
nondigit: one of
  a b c d e f g h i j k l m
  n o p q r s t u v w x y z
  A B C D E F G H I J K L M
  N O P Q R S T U V W X Y Z _
digit: one of
  0 1 2 3 4 5 6 7 8 9

Что является производной общей схемы [a-zA-Z_][a-zA-Z0-9_]*, с которой мы все знакомы. Как видите, это не позволяет class foo::bar; быть действительным предварительным объявлением, поскольку foo::bar не является идентификатором. Это полностью определенное имя, что-то другое.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...