Dlaczego nie mogę przesłać dalej klasy w przestrzeni nazw przy użyciu podwójnych dwukropków?

164
class Namespace::Class;

Dlaczego muszę to zrobić ?:

namespace Namespace {
    class Class;
}

Używając VC ++ 8.0, kompilator problemy:

błąd C2653: „Przestrzeń nazw”: nie jest nazwą klasy ani przestrzeni nazw

Zakładam, że problem polega na tym, że kompilator nie może stwierdzić, czy Namespacejest to klasa czy przestrzeń nazw? Ale dlaczego to ma znaczenie, skoro jest to tylko deklaracja wyprzedzająca?

Czy istnieje inny sposób zadeklarowania klasy zdefiniowanej w jakiejś przestrzeni nazw? Powyższa składnia sprawia wrażenie, że „ponownie otwieram” przestrzeń nazw i rozszerzam jej definicję. A co by Classbyło, gdyby tak naprawdę nie zostały zdefiniowane w Namespace? Czy w pewnym momencie spowoduje to błąd?

Yong Li
źródło
44
Nie zgadzam się ze wszystkimi odpowiedziami tutaj i powiem, że to tylko błąd projektowy języka. Mogliby lepiej myśleć.
Pavel Radzivilovsky
To prowadzi do dyskusji, dlaczego jest to nielegalne w C ++ (co jest subiektywne) i wygląda na kłótliwe. Głosowanie za zamknięciem.
David Thornley,
7
W jaki sposób kompilator wiedzieć, że w jest identyfikatorem nazw zamiast nazwa klasy? A::BA
David R Tribble
@STingRaySC: Dyskusja jest subiektywna, ponieważ nie ma jasnej odpowiedzi, dlaczego C ++ to robi, więc spekulujemy. (Pytanie jest pytaniem o strzelbę, z kilkoma obiektywnymi odpowiedziami, na które już udzielono odpowiedzi.) W tym momencie stałem się wrażliwy na ślady kłótni, a twoja zgoda z Pawłem, że jest to wada C ++, kwalifikuje się. Nie miałbym problemu z pytaniem, dlaczego ma to znaczenie, czy Namespacejest to klasa czy przestrzeń nazw. Po prostu nie zbliżaj się do śladu możliwości rozpoczęcia wojny o język o składnię.
David Thornley

Odpowiedzi:

85

Ponieważ nie możesz. W języku C ++ w pełni kwalifikowane nazwy są używane tylko w odniesieniu do istniejących (tj. Wcześniej zadeklarowanych) jednostek. Nie można ich używać do wprowadzania nowych bytów.

I w rzeczywistości „wznowienie” przestrzeń nazw zadeklarować nowych podmiotów. Jeśli klasa Classzostanie później zdefiniowana jako członek innej przestrzeni nazw - jest to zupełnie inna klasa, która nie ma nic wspólnego z tą, którą tutaj zadeklarowałeś.

Gdy dojdziesz do punktu definiowania wstępnie zadeklarowanej klasy, nie musisz ponownie „otwierać” przestrzeni nazw. Możesz zdefiniować go w globalnej przestrzeni nazw (lub dowolnej przestrzeni nazw obejmującej twoją Namespace) jako

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

Ponieważ odwołujesz się do jednostki, która została już zadeklarowana w przestrzeni nazw Namespace, możesz użyć nazwy kwalifikowanej Namespace::Class.

Mrówka
źródło
10
@STingRaySC: Jedynym sposobem zadeklarowania w przód klasy zagnieżdżonej jest umieszczenie deklaracji wewnątrz definicji klasy otaczającej. I rzeczywiście nie ma sposobu na zadeklarowanie klasy zagnieżdżonej przed definicją klasy otaczającej.
AnT
@STingRaySC: Zagnieżdżoną klasę można zadeklarować fwd - zobacz moją odpowiedź.
John Dibling
8
@John Dibling: Klasa zagnieżdżona to klasa zadeklarowana wewnątrz innej klasy. Klasa zadeklarowana bezpośrednio w przestrzeni nazw nie jest klasą zagnieżdżoną. W twojej odpowiedzi nie ma nic o klasach sensteda.
AnT
198

Otrzymujesz poprawne odpowiedzi, po prostu spróbuję przeredagować:

class Namespace::Class;

Dlaczego muszę to zrobić?

Musisz to zrobić, ponieważ termin Namespace::Classmówi kompilatorowi:

... OK, kompilatorze. Znajdź przestrzeń nazw o nazwie Namespace, aw niej odwołaj się do klasy o nazwie Class.

Ale kompilator nie wie, o czym mówisz, ponieważ nie zna nazwanej przestrzeni nazw Namespace. Nawet jeśli istniałaby przestrzeń nazw o nazwie Namespace, jak w:

namespace Namespace
{
};

class Namespace::Class;

to nadal nie zadziała, ponieważ nie możesz zadeklarować klasy w przestrzeni nazw spoza tej przestrzeni. Musisz znajdować się w przestrzeni nazw.

Tak więc możesz w rzeczywistości zadeklarować klasę w przestrzeni nazw. Po prostu zrób to:

namespace Namespace
{
    class Class;
};
John Dibling
źródło
39
Wszystkie inne odpowiedzi były dla mnie mylące, ale to „nie możesz zadeklarować klasy w przestrzeni nazw spoza tej przestrzeni. Musisz znajdować się w przestrzeni nazw”. była bardzo pomocna wskazówka do zapamiętania.
dashesy
22

Przypuszczam, że z tego samego powodu nie można za jednym razem zadeklarować zagnieżdżonych przestrzeni nazw:

namespace Company::Communications::Sockets {
}

i musisz to zrobić:

namespace Company {
  namespace Communications {
    namespace Sockets {
    }
  }
}
Igor Zevaka
źródło
1
W rzeczywistości nie jest to odpowiedź wyjaśniająca, dlaczego nie możesz tego zrobić.
StarPilot
6
To jest odpowiedź, która zaoszczędziła mi dużo czasu
Kadir Erdem Demir
17
C ++ 17 dodaje to.
rparolin
Tutaj wiesz, że wszystkie są przestrzeniami nazw. Ale z klasą Company :: Communications :: Socket nie wiadomo, czy Communications jest przestrzenią nazw, czy klasą (gdzie gniazdo jest klasą zagnieżdżoną).
Lothar
12

Nie byłoby jasne, jaki właściwie jest typ zmiennej zadeklarowanej w przód. Deklaracja do przodu class Namespace::Class;może oznaczać

namespace Namespace {
  class Class;
}

lub

class Namespace {
public:
  class Class;
};
Martin G.
źródło
6
Myślę, że jest to jedna z najlepszych odpowiedzi, ponieważ odpowiada, dlaczego sam kompilator nie może tego łatwo określić.
Devolus,
1

Istnieje wiele doskonałych odpowiedzi na temat uzasadnienia odrzucenia tego. Chcę tylko przedstawić nudną standardową klauzulę, która wyraźnie tego zabrania. Dotyczy to C ++ 17 (n4659).

Wspomniany akapit to [nazwa.klasy] / 2 :

Deklaracja składająca się wyłącznie z identyfikatora klucza klasy ; jest ponowną deklaracją nazwy w bieżącym zakresie lub przekazaną dalej deklaracją identyfikatora jako nazwy klasy. Wprowadza nazwę klasy do bieżącego zakresu.

Powyższe definiuje, co stanowi deklarację forward (lub powtórną deklarację klasy). W istocie, musi ona być jeden class identifier;, struct identifier;lub union identifier;gdzie identifer jest wspólnym słownikowa definicja w [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

To jest produkcja wspólnego schematu, [a-zA-Z_][a-zA-Z0-9_]*który wszyscy znamy. Jak widać, wyklucza to class foo::bar;bycie prawidłową deklaracją przekazania, ponieważ foo::barnie jest to identyfikator. To w pełni kwalifikowana nazwa, coś innego.

StoryTeller - Unslander Monica
źródło