2010-01-13 8 views
131
class Namespace::Class; 

¿Por qué tengo que hacer esto ?:¿Por qué no puedo reenviar-declarar una clase en un espacio de nombres usando dos puntos y coma?

namespace Namespace { 
    class Class; 
} 

usando VC++ 8.0, los problemas del compilador:

error C2653: 'Espacio de nombres': no ​​es un nombre de clase o espacio de nombres

Supongo que el problema aquí es que el compilador no puede decir si Namespace es una clase o un espacio de nombres? Pero, ¿por qué es importante ya que es solo una declaración avanzada?

¿Hay alguna otra manera de reenviar-declarar una clase definida en algún espacio de nombres? La sintaxis anterior se siente como si estuviera "reabriendo" el espacio de nombres y ampliando su definición. ¿Qué pasa si Class no fueron realmente definidos en Namespace? ¿Esto resultaría en un error en algún momento?

+30

Déjenme estar en desacuerdo con todas las respuestas aquí, y decir que es simplemente un error de diseño del idioma. Podrían pensar mejor. –

+0

Esto tiende a una discusión de por qué esto es ilegal en C++ (que es subjetivo), y se ve argumentativo. Votando para cerrar. –

+6

¿Cómo se supone que el compilador sabe que en 'A :: B' el' A' es un identificador de espacio de nombres en lugar de un nombre de clase? –

Respuesta

72

Porque no se puede. En el lenguaje C++, los nombres totalmente calificados solo se utilizan para referirse a entidades existentes (es decir, declaradas previamente). No se pueden usar para introducir nuevas entidades.

Y usted es "reabriendo" de hecho el espacio de nombres para declarar nuevas entidades. Si la clase Class se define posteriormente como miembro de un espacio de nombres diferente, es una clase completamente diferente que no tiene nada que ver con la que ha declarado aquí.

Una vez que llegue al punto de que define la clase preestablecida, no necesita volver a abrir el espacio de nombres. Puede definirlo en el espacio de nombres global (o cualquier espacio de nombres que encierra su Namespace) como

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

Dado que usted se refiere a una entidad que ya ha sido declarado en espacio de nombres Namespace, puede utilizar el nombre cualificado Namespace::Class.

+10

@STingRaySC: La única forma de reenviar-declarar una clase anidada es colocar la declaración dentro de la * definición * de la clase adjunta. Y de hecho no hay forma de reenviar: declarar la clase anidada antes de la definición de la clase adjunta. – AnT

+0

@STingRaySC: una clase anidada se puede declarar ahora - ver mi respuesta. –

+6

@John Dibling: * Clase anidada * es una clase declarada dentro de otra clase. Una clase declarada inmediatamente dentro de un espacio de nombres no es una clase anidada. No hay nada sobre clases sensibles en tu respuesta. – AnT

161

Usted está recibiendo respuestas correctas, permítanme tratar de re-redacción:

class Namespace::Class;

¿Por qué tengo que hacer esto?

que tiene que hacer esto porque el término Namespace::Class está diciendo al compilador:

... OK, compilador. Vaya a buscar el espacio de nombres llamado Namespace, y dentro de que se refieren a la clase llamada Class.

Pero el compilador no sabe de lo que está hablando porque no conoce ningún espacio de nombres llamado Namespace.Incluso si hubiera un espacio de nombres llamado Namespace, como en:

namespace Namespace 
{ 
}; 

class Namespace::Class; 

todavía no funcionaría, porque no se puede declarar una clase dentro de un espacio de nombres desde fuera ese espacio de nombres. Tienes que estar en el espacio de nombres.

Así que, de hecho, puede reenviar declarar una clase dentro de un espacio de nombres. Sólo hacer esto:

namespace Namespace 
{ 
    class Class; 
}; 
+29

Todas las demás respuestas me resultaron confusas, pero esto "no se puede declarar una clase dentro de un espacio de nombres desde fuera del espacio de nombres. Tiene que estar en el espacio de nombres". fue una sugerencia muy útil para recordar. – dashesy

17

supongo que es por la misma razón no se puede declarar espacios de nombres anidados de una sola vez de esta manera:

namespace Company::Communications::Sockets { 
} 

y usted tiene que hacer esto:

namespace Company { 
    namespace Communications { 
    namespace Sockets { 
    } 
    } 
} 
+0

Esto no es en realidad una respuesta que explique por qué no puede hacerlo. – StarPilot

+4

Esta es una respuesta que salvó gran parte de mi tiempo –

+0

Esto merece más votos por votos –

0

Hay muchas respuestas excelentes sobre la lógica implicada en rechazarlo. Solo quiero proporcionar la aburrida cláusula standardese que específicamente lo prohíbe. Esto es válido para C++ 17 (n4659).

El párrafo en cuestión es [class.name]/2:

Una declaración que consiste únicamente en identificador de clave de clase; es una redeclaración del nombre en el ámbito actual o una declaración de identificador como nombre de clase. Introduce el nombre de la clase en el alcance actual.

Lo anterior define lo que constituye una declaración directa (o redclaration de una clase). En esencia, tiene que ser uno de class identifier;, struct identifier; o union identifier; donde identificador es la definición léxica común en [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 

¿Cuál es la producción del régimen común [a-zA-Z_][a-zA-Z0-9_]* todos estamos familiarizados. Como puede ver, esto excluye que class foo::bar; sea una declaración directa adelantada, porque foo::bar no es un identificador. Es un nombre completamente calificado, algo diferente.

Cuestiones relacionadas