c++ - reglas - ¿Por qué no puedo reenviar-declarar una clase en un espacio de nombres usando dos puntos y coma?
uso del punto (4)
class Namespace::Class;
¿Por qué tengo que hacer esto ?:
namespace Namespace {
class Class;
}
Usando VC ++ 8.0, el compilador emite:
error C2653: ''Namespace'': no es una clase o nombre de 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 forma 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 la Class
no se definió realmente en Namespace
? ¿Esto resultaría en un error en algún momento?
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 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 alcance actual.
Lo anterior define lo que constituye una declaración directa (o declaración en rojo de una clase). Básicamente, debe ser uno de class identifier;
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 [a-zA-Z_][a-zA-Z0-9_]*
que todos estamos familiarizados? Como puede ver, esto excluye la class foo::bar;
de ser una declaración forward válida, porque foo::bar
no es un identificador. Es un nombre completamente calificado, algo diferente.
Obtendrá las respuestas correctas, permítame intentar una nueva redacción:
class Namespace::Class;
¿Por qué tengo que hacer esto?
Tienes que hacer esto porque el término Namespace::Class
le dice al compilador:
... OK, compilador. Ve a buscar el espacio de nombre llamado Namespace, y dentro de ese 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;
aún 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.
Entonces, de hecho, puede reenviar declarar una clase dentro de un espacio de nombres. Solo haz esto:
namespace Namespace
{
class Class;
};
Porque no puedes. En lenguaje C ++ los nombres completamente calificados solo se usan para referirse a entidades existentes (es decir, declaradas previamente). No se pueden usar para introducir nuevas entidades.
Y de hecho está "reabriendo" el espacio de nombres para declarar nuevas entidades. Si la clase Class
se define más tarde como 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 llega al punto de definir la clase preestablecida, no necesita "volver a abrir" el espacio de nombres nuevamente. Puede definirlo en el espacio de nombres global (o cualquier espacio de nombres que contenga su Namespace
) como
class Namespace::Class {
/* whatever */
};
Como se refiere a una entidad que ya ha sido declarada en el espacio de Namespace
espacio de Namespace
, puede usar el nombre calificado Namespace::Class
.
Supongo que es por la misma razón por la que no puede declarar espacios de nombres anidados de la siguiente manera:
namespace Company::Communications::Sockets {
}
y tienes que hacer esto:
namespace Company {
namespace Communications {
namespace Sockets {
}
}
}