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

164
class Namespace::Class;

Зачем мне это делать?

namespace Namespace {
    class Class;
}

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

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

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

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

Ён ли
источник
44
Позвольте мне не согласиться со всеми ответами здесь и сказать, что это просто ошибка дизайна языка. Они могли бы думать лучше.
Павел Радзивиловский
Это ведет к обсуждению того, почему это недопустимо в C ++ (это субъективно), и выглядит аргументно. Голосование закрыть.
Дэвид Торнли
7
Как компилятор должен знать , что в является идентификатором пространства имен вместо имени класса? A::BA
Дэвид Р. Триббл
@STingRaySC: обсуждение субъективно, поскольку нет четкого ответа, почему C ++ делает это, поэтому мы размышляем. (Этот вопрос является вопросом дробовика, с некоторыми вопросами с объективными ответами, на которые уже дан ответ.) В этот момент я становлюсь чувствительным к следам аргументов, и ваше согласие с Павлом в том, что это ошибка в C ++, квалифицируется. У меня не было бы проблем с вопросом, почему это важно, если Namespaceэто класс или пространство имен. Только не приближайтесь к намеку о возможности предположительно начать языковую войну за синтаксис.
Дэвид Торнли

Ответы:

85

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

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

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

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

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

Муравей
источник
10
@STingRaySC: Единственный способ объявить вложенный класс в прямом направлении - это поместить объявление в определение включающего класса. И действительно, нет способа заранее объявить вложенный класс до определения включающего класса.
AnT
@STingRaySC: вложенный класс может быть объявлен fwd - см. Мой ответ.
Джон Диблинг
8
@John Dibling: вложенный класс - это класс, объявленный внутри другого класса. Класс, объявленный непосредственно внутри пространства имен, не является вложенным классом. В твоем ответе нет ничего о воспринятых классах
AnT
198

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

class Namespace::Class;

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

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

... ОК, компилятор. Найдите и найдите пространство имен с именем Namespace, и внутри него обратитесь к классу с именем Class.

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

namespace Namespace
{
};

class Namespace::Class;

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

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

namespace Namespace
{
    class Class;
};
Джон Диблинг
источник
39
Все остальные ответы меня смущали, но это «вы не можете объявить класс в пространстве имен за пределами этого пространства имен. Вы должны быть в пространстве имен». была очень полезная подсказка для запоминания.
дашесы
22

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

namespace Company::Communications::Sockets {
}

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

namespace Company {
  namespace Communications {
    namespace Sockets {
    }
  }
}
Игорь Зевака
источник
1
На самом деле это не ответ, объясняющий, почему вы не можете этого сделать.
StarPilot
6
Это ответ, который сэкономил мне много времени
Кадир Эрдем Демир,
17
C ++ 17 добавляет это.
rparolin
Здесь вы знаете, что все это пространства имен. Но с классом Company :: Communications :: Socket вы не знаете, является ли Communications пространством имен или классом (где socket - это вложенный класс).
Lothar
12

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

namespace Namespace {
  class Class;
}

или

class Namespace {
public:
  class Class;
};
Мартин Г
источник
6
Я думаю, что это один из лучших ответов, потому что он отвечает, почему это не может быть легко определено самим компилятором.
Деволус
1

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

Рассматриваемый абзац - [class.name] / 2 :

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

Выше определено, что составляет предварительное объявление (или объявление класса). По сути, он должен быть одним из class identifier;, struct identifier;или union 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не является идентификатором. Это полностью определенное имя, что-то другое.

Рассказчик - Unslander Monica
источник