web-dev-qa-db-fra.com

Pourquoi ne puis-je pas déclarer une classe dans un espace de noms en utilisant le double point-virgule?

class Namespace::Class;

Pourquoi dois-je faire cela ?:

namespace Namespace {
    class Class;
}

À l’aide de VC++ 8.0, le compilateur pose les problèmes suivants:

erreur C2653: 'Espace de noms': n'est pas un nom de classe ou d'espace de noms

Je suppose que le problème ici est que le compilateur ne peut pas dire si Namespace est une classe ou un espace de noms? Mais pourquoi est-ce important, puisqu'il ne s'agit que d'une déclaration anticipée?

Existe-t-il un autre moyen de déclarer en aval une classe définie dans un espace de noms? La syntaxe ci-dessus donne l'impression de "rouvrir" l'espace de noms et d'en étendre la définition. Et si Class n'était pas réellement défini dans Namespace? Cela entraînerait-il une erreur à un moment donné?

148
user123456

Parce que tu ne peux pas. En langage C++, les noms complets ne sont utilisés que pour faire référence à des entités existantes (c'est-à-dire précédemment déclarées). Ils ne peuvent pas être utilisés pour introduire new entités.

Et vous êtes en fait "rouvrez" l'espace de noms pour déclarer de nouvelles entités. Si la classe Class est ultérieurement définie en tant que membre d'un espace de noms différent, il s'agit d'une classe complètement différente qui n'a rien à voir avec celle que vous avez déclarée ici.

Une fois que vous avez atteint le point définissant la classe déclarée, vous n'avez plus besoin de "rouvrir" l'espace de noms. Vous pouvez le définir dans l’espace de noms global (ou dans tout autre espace de noms contenant votre Namespace) en tant que

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

Puisque vous vous référez à une entité déjà déclarée dans l'espace de noms Namespace, vous pouvez utiliser le nom qualifié Namespace::Class.

79
AnT

Vous obtenez des réponses correctes, laissez-moi juste essayer de reformuler:

class Namespace::Class;

Pourquoi dois-je faire cela?

Vous devez le faire parce que le terme Namespace::Class indique au compilateur:

... OK, compilateur. Allez trouver le namespace nommé Namespace, et au sein de qui font référence à la classe nommée Classe.

Mais le compilateur ne sait pas de quoi vous parlez, car il ne connaît aucun espace de nommage nommé Namespace. Même s'il y avait un espace de noms nommé Namespace, comme dans:

namespace Namespace
{
};

class Namespace::Class;

cela ne fonctionnerait toujours pas, car vous ne pouvez pas déclarer une classe dans un espace de noms de l'extérieur de cet espace de noms. Vous devez être dans l'espace de noms.

Ainsi, vous pouvez réellement déclarer une classe dans un espace de noms. Faites juste ceci:

namespace Namespace
{
    class Class;
};
179
John Dibling

Je suppose que c'est pour la même raison que vous ne pouvez pas déclarer les espaces de noms imbriqués d'un seul coup comme ceci:

namespace Company::Communications::Sockets {
}

et vous devez faire ceci:

namespace Company {
  namespace Communications {
    namespace Sockets {
    }
  }
}
19
Igor Zevaka

Le type d'une variable déclarée à terme ne serait pas clair. La déclaration forward class Namespace::Class; pourrait signifier

namespace Namespace {
  class Class;
}

ou

class Namespace {
public:
  class Class;
};
2
Martin G

Il y a beaucoup d'excellentes réponses sur les raisons invoquées pour le refuser. Je veux juste fournir la clause standard ennuyeuse qui l’interdit spécifiquement. Cela est vrai pour C++ 17 (n4659).

Le paragraphe en question est [class.name]/2 :

Une déclaration composée uniquement de l'identifiant class-key; est soit un redéclaration du nom dans la portée actuelle ou un transfert déclaration de l'identifiant en tant que nom de classe. Il introduit la classe nom dans la portée actuelle.

Ce qui précède définit ce qui constitue une déclaration aval (ou redclaration d'une classe). Pour l'essentiel, il doit s'agir de l'un des class identifier;, struct identifier; ou union identifier;identifer est la définition lexicale commune dans [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

Qui est la production du schéma commun [a-zA-Z_][a-zA-Z0-9_]* que nous connaissons tous. Comme vous pouvez le constater, cela empêche que class foo::bar; soit une déclaration de transfert valide, car foo::bar n'est pas un identifiant. C'est un nom complet, quelque chose de différent.

0
StoryTeller