Perché non posso dichiarare in avanti una classe in uno spazio dei nomi usando due punti e virgola?


164
class Namespace::Class;

Perché devo farlo ?:

namespace Namespace {
    class Class;
}

Utilizzando VC ++ 8.0, il compilatore genera:

errore C2653: "Spazio dei nomi": non è un nome di classe o di spazio dei nomi

Presumo che il problema qui sia che il compilatore non può dire se Namespaceè una classe o uno spazio dei nomi? Ma perché è importante dato che è solo una dichiarazione in avanti?

C'è un altro modo per dichiarare in avanti una classe definita in alcuni spazi dei nomi? La sintassi sopra sembra che sto "riaprendo" lo spazio dei nomi e ne estendo la definizione. E se Classnon fossero stati effettivamente definiti in Namespace? Ciò comporterebbe un errore ad un certo punto?


44
Vorrei essere in disaccordo con tutte le risposte qui e dire che è solo un bug di progettazione del linguaggio. Potrebbero pensare meglio.
Pavel Radzivilovsky,

Questo tende a discutere sul perché ciò sia illegale in C ++ (che è soggettivo) e sembra polemico. Votare per chiudere.
David Thornley,

7
Come si suppone che il compilatore a sapere che in A::Bla Aè un identificatore spazio dei nomi al posto di un nome di classe?
David R Tribble,

@STingRaySC: La discussione è soggettiva in quanto non esiste una risposta chiara sul perché C ++ faccia questo, quindi stiamo ipotizzando. (La domanda è una domanda da fucile a pompa, con alcune domande con risposte obiettive, a cui è già stata data una risposta.) A quel punto, divento sensibile alle tracce di argomentazioni e il tuo accordo con Pavel sul fatto che si tratti di una cattiva funzionalità del C ++ si qualifica. Non avrei problemi con una domanda sul perché sia ​​importante se si Namespacetratta di una classe o di uno spazio dei nomi. Basta non avvicinarsi affatto al suggerimento di una possibilità di iniziare una guerra di fiamma di linguaggio sulla sintassi.
David Thornley,

Risposte:


85

Perché non puoi. Nel linguaggio C ++ i nomi completi sono usati solo per riferirsi a entità esistenti (cioè precedentemente dichiarate). Non possono essere usati per introdurre nuove entità.

E voi siete in realtà "riapertura" lo spazio dei nomi per dichiarare nuove entità. Se la classe Classviene successivamente definita come membro di un diverso spazio dei nomi, è una classe completamente diversa che non ha nulla a che fare con quella dichiarata qui.

Una volta arrivato al punto di definire la classe pre-dichiarata, non è necessario "riaprire" nuovamente lo spazio dei nomi. Puoi definirlo nello spazio dei nomi globale (o in qualsiasi spazio dei nomi che racchiude il tuo Namespace) come

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

Dato che ti riferisci a un'entità che è già stata dichiarata nello spazio dei nomi Namespace, puoi usare un nome qualificato Namespace::Class.


10
@STingRaySC: l'unico modo per dichiarare in avanti una classe nidificata è inserire la dichiarazione all'interno della definizione della classe che la racchiude. E in effetti non c'è modo di dichiarare in avanti la classe nidificata prima della definizione della classe che la racchiude.
AnT

@STingRaySC: è possibile dichiarare una classe nidificata - vedi la mia risposta.
John Dibling,

8
@ John Dibling: la classe nidificata è una classe dichiarata all'interno di un'altra classe. Una classe dichiarata immediatamente all'interno di uno spazio dei nomi non è una classe nidificata. Non c'è niente riguardo alle lezioni sensate nella tua risposta.
An

198

Stai ricevendo risposte corrette, fammi solo provare a riformulare:

class Namespace::Class;

Perché devo farlo?

Devi farlo perché il termine Namespace::Classindica al compilatore:

... OK, compilatore. Vai a trovare lo spazio dei nomi chiamato Namespace, e all'interno di quello si riferiscono alla classe denominata Class.

Ma il compilatore non sa di cosa stai parlando perché non conosce nessuno spazio dei nomi chiamato Namespace. Anche se esistesse uno spazio dei nomi chiamato Namespace, come in:

namespace Namespace
{
};

class Namespace::Class;

non funzionerebbe ancora, perché non puoi dichiarare una classe all'interno di uno spazio dei nomi al di fuori di quello spazio dei nomi. Devi essere nello spazio dei nomi.

Quindi, puoi in effetti dichiarare una classe all'interno di uno spazio dei nomi. Basta fare questo:

namespace Namespace
{
    class Class;
};

39
Tutte le altre risposte mi confondevano ma questo "non puoi dichiarare una classe all'interno di uno spazio dei nomi al di fuori di quello spazio dei nomi. Devi essere nello spazio dei nomi". è stato un suggerimento molto utile da ricordare.
dashesy

22

Suppongo che sia per lo stesso motivo per cui non puoi dichiarare spazi dei nomi nidificati in una sola volta in questo modo:

namespace Company::Communications::Sockets {
}

e devi fare questo:

namespace Company {
  namespace Communications {
    namespace Sockets {
    }
  }
}

1
Questa non è in realtà una risposta che spiega perché non puoi farlo.
StarPilot,

6
Questa è una risposta che mi ha
permesso di

17
C ++ 17 aggiunge questo.
rparolin,

Qui sai che tutti sono spazi dei nomi. Ma con la classe Company :: Communications :: Socket non sai se Communications è uno spazio dei nomi o una classe (dove socket è la classe nidificata).
Lothar,

12

Non sarebbe chiaro quale sia effettivamente il tipo di una variabile dichiarata in avanti. La dichiarazione anticipata class Namespace::Class;potrebbe significare

namespace Namespace {
  class Class;
}

o

class Namespace {
public:
  class Class;
};


6
Penso che questa sia una delle migliori risposte, perché risponde al perché questo non può essere facilmente determinato dal compilatore stesso.
Devolus,

1

Ci sono molte risposte eccellenti sulla logica alla base della sua esclusione. Voglio solo fornire la noiosa clausola standardese che la proibisce espressamente. Questo vale per C ++ 17 (n4659).

Il paragrafo in questione è [class.name] / 2 :

Una dichiarazione costituita esclusivamente da un identificatore di chiave di classe ; è una nuova dichiarazione del nome nell'ambito corrente o una dichiarazione in avanti dell'identificatore come nome di classe. Introduce il nome della classe nell'ambito corrente.

Quanto sopra definisce ciò che costituisce una dichiarazione a termine (o redclaration di una classe). In sostanza, deve essere uno dei class identifier;, struct identifier;o union identifier;dove identifer è la definizione lessicale comune in [nome lex] :

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

Qual è la produzione dello schema comune che [a-zA-Z_][a-zA-Z0-9_]*conosciamo tutti. Come puoi vedere, ciò preclude class foo::bar;di essere una dichiarazione in avanti valida, perché foo::barnon è un identificatore. È un nome completo, qualcosa di diverso.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.