¿Por qué no puedo declarar hacia adelante una clase en un espacio de nombres usando dos puntos dobles?

164
class Namespace::Class;

¿Por qué tengo que hacer esto ?:

namespace Namespace {
    class Class;
}

Usando VC ++ 8.0, el compilador emite:

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 Namespacees una clase o un espacio de nombres. Pero, ¿por qué importa esto ya que es solo una declaración adelantada?

¿Hay otra forma de declarar hacia adelante una clase definida en algún espacio de nombres? La sintaxis anterior parece que estoy "reabriendo" el espacio de nombres y extendiendo su definición. ¿Qué pasa si Classno se definió realmente en Namespace? ¿Esto resultaría en un error en algún momento?

Yong Li
fuente
44
Permítanme estar en desacuerdo con todas las respuestas aquí, y decir que es simplemente un error de diseño del lenguaje. Podrían pensar mejor.
Pavel Radzivilovsky
Esto tiende a una discusión de por qué esto es ilegal en C ++ (que es subjetivo), y parece discutidor. Votación para cerrar.
David Thornley el
77
¿Cómo se supone que el compilador para saber que en A::Bel Aes un identificador de espacio de nombres en lugar de un nombre de clase?
David R Tribble
@STingRaySC: La discusión es subjetiva ya que no hay una respuesta clara de por qué C ++ hace esto, por lo que estamos especulando. (La pregunta es una pregunta de escopeta, con algunas preguntas con respuestas objetivas, que ya han sido respondidas). En ese momento, me vuelvo sensible a los rastros de argumento, y su acuerdo con Pavel de que esta es una mala característica de C ++ califica. No tendría ningún problema con una pregunta de por qué importa si Namespacees una clase o un espacio de nombres. Simplemente no se acerque al indicio de una posibilidad de iniciar una guerra de lenguaje sobre la sintaxis.
David Thornley

Respuestas:

85

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

Y usted está de hecho "reapertura" del espacio de nombres para declarar nuevas entidades. Si la clase Classse define más tarde como un miembro de un espacio de nombres diferente, es una clase completamente diferente que no tiene nada que ver con la que usted declaró aquí.

Una vez que llegue al punto de definir la clase pre-declarada, no necesita "volver a abrir" el espacio de nombres nuevamente. Puede definirlo en el espacio de nombres global (o en cualquier espacio de nombres que incluya su Namespace) como

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

Como se refiere a una entidad que ya ha sido declarada en el espacio de nombres Namespace, puede usar el nombre calificado Namespace::Class.

Hormiga
fuente
10
@STingRaySC: La única forma de declarar hacia adelante una clase anidada es colocar la declaración dentro de la definición de la clase de cierre. Y, de hecho, no hay forma de declarar hacia adelante la clase anidada antes de la definición de la clase de cierre.
ANT
@STingRaySC: una clase anidada se puede declarar fwd; vea mi respuesta.
John Dibling el
8
@ John Dibling: la 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 las clases sensoriales en tu respuesta.
ANT
198

Estás obteniendo respuestas correctas, déjame intentar una nueva redacción:

class Namespace::Class;

¿Por qué tengo que hacer esto?

Tienes que hacer esto porque el término Namespace::Classle dice al compilador:

... OK, compilador. Busque el espacio de nombres llamado Espacio de nombres, y dentro de eso, refiérase a la clase llamada Clase.

Pero el compilador no sabe de qué está hablando porque no conoce ningún espacio de nombres con nombre Namespace. Incluso si hubiera un espacio de nombres nombrado Namespace, como en:

namespace Namespace
{
};

class Namespace::Class;

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

Por lo tanto, puede declarar una clase dentro de un espacio de nombres. Solo haz esto:

namespace Namespace
{
    class Class;
};
John Dibling
fuente
39
Todas las demás respuestas me resultaron confusas, pero esto "no se puede declarar una clase dentro de un espacio de nombres desde fuera de ese espacio de nombres. Debe estar en el espacio de nombres". Fue una pista muy útil para recordar.
guiones
22

Supongo que es por la misma razón por la que no puede declarar espacios de nombres anidados de una sola vez así:

namespace Company::Communications::Sockets {
}

y tienes que hacer esto:

namespace Company {
  namespace Communications {
    namespace Sockets {
    }
  }
}
Igor Zevaka
fuente
1
En realidad, esta no es una respuesta que explique por qué no puede hacerlo.
StarPilot
66
Esta es una respuesta que me ahorró una gran cantidad de tiempo
Kadir Erdem Demir el
17
C ++ 17 agrega esto.
rparolin
Aquí sabes que todos son espacios de nombres. Pero con la clase Company :: Communications :: Socket no sabes si Communications es un espacio de nombres o una clase (donde socket es la clase anidada).
Lothar
12

No estaría claro cuál es realmente el tipo de una variable declarada hacia adelante. La declaración adelantada class Namespace::Class;podría significar

namespace Namespace {
  class Class;
}

o

class Namespace {
public:
  class Class;
};
Martin G
fuente
66
Creo que esta es una de las mejores respuestas, porque responde por qué esto no puede ser fácilmente determinado por el compilador.
Devolus
1

Hay muchas respuestas excelentes sobre la justificación involucrada en no permitirlo. Solo quiero proporcionar la aburrida cláusula estándar que específicamente lo prohíbe. Esto es cierto para C ++ 17 (n4659).

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

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

Lo anterior define lo que constituye una declaración directa (o una declaración roja de una clase). Esencialmente, debe ser uno de class identifier;, struct identifier;o union identifier;donde identifer 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 esquema común con el [a-zA-Z_][a-zA-Z0-9_]*que todos estamos familiarizados. Como puede ver, esto impide class foo::bar;ser una declaración directa válida, ya foo::barque no es un identificador. Es un nombre totalmente calificado, algo diferente.

StoryTeller - Unslander Monica
fuente