Чому я не можу переадресувати оголошення в просторі імен за допомогою подвійних кольорових знаків?


164
class Namespace::Class;

Чому я повинен це робити ?:

namespace Namespace {
    class Class;
}

Використовуючи VC ++ 8.0, компілятор видає:

помилка C2653: 'Простір імен': це не ім’я класу чи простору імен

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

Чи є інший спосіб переадресувати оголошення класу, визначеного в якомусь просторі імен? Синтаксис вище відчуває, що я "знову відкриваю" простір імен і розширюю його визначення. Що робити, якщо Classнасправді не було визначено Namespace? Чи призведе це до помилки в якийсь момент?


44
Дозвольте мені не погодитися з усіма відповідями тут і скажу, що це лише помилка дизайну мови. Вони могли б думати краще.
Павло Радзівіловський

Це спрямоване на обговорення того, чому це нелегально в C ++ (що є суб'єктивним), і це виглядає аргументативно. Голосування про закриття.
Девід Торнлі

7
Як компілятор повинен знати , що в є ідентифікатором простору імен замість імені класу? A::BA
David R Tribble

@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говорить компілятору:

... Добре, компілятор. Перейдіть, знайдіть простір імен з назвою Простір імен, і всередині цього зверніться до класу з назвою 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 додає це.
рпаролін

Тут ви знаєте, що всі це простори імен. Але з класом Company :: Communications :: Socket ви не знаєте, чи Communications є простором імен або класом (де сокет - це вкладений клас).
Лотар

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не є ідентифікатором. Це цілком кваліфіковане ім’я, щось інше.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.