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


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.