Pourquoi ne puis-je pas déclarer en avant une classe dans un espace de noms en utilisant des doubles virgules?

164
class Namespace::Class;

Pourquoi dois-je faire cela?:

namespace Namespace {
    class Class;
}

À l'aide de VC ++ 8.0, le compilateur émet:

erreur C2653: 'Namespace': 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 s'il Namespaces'agit d'une classe ou d'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 avant une classe définie dans un espace de noms? La syntaxe ci-dessus donne l'impression que je "rouvre" l'espace de noms et étend sa définition. Et si Classn'étaient pas réellement définis dans Namespace? Cela entraînerait-il une erreur à un moment donné?

Yong Li
la source
44
Permettez-moi de ne pas être d'accord avec toutes les réponses ici et de dire que c'est simplement un bug de conception du langage. Ils pourraient mieux penser.
Pavel Radzivilovsky
Cela tend vers une discussion sur les raisons pour lesquelles cela est illégal en C ++ (ce qui est subjectif), et cela semble argumentatif. Vote de clôture.
David Thornley
7
Comment le compilateur est-il censé savoir que dans A::Ble Aest un identifiant d'espace de noms au lieu d'un nom de classe?
David R Tribble du
@STingRaySC: La discussion est subjective dans la mesure où il n'y a pas de réponse claire pourquoi C ++ fait cela, donc nous spéculons. (La question est une question de fusil de chasse, avec quelques questions avec des réponses objectives, auxquelles on a déjà répondu.) À ce stade, je deviens sensible aux traces d'argumentation, et votre accord avec Pavel sur le fait qu'il s'agit d'une erreur de C ++ est nuancé. Je n'aurais aucun problème avec une question de savoir pourquoi il est important que ce Namespacesoit une classe ou un espace de noms. Ne vous approchez pas de la possibilité de déclencher une guerre des flammes du langage sur la syntaxe.
David Thornley

Réponses:

85

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 de nouvelles entités.

Et vous êtes en fait « réouverture » l'espace de noms de déclarer de nouvelles entités. Si la classe Classest définie plus tard comme 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 êtes arrivé au point de définir la classe pré-déclarée, vous n'avez pas besoin de «rouvrir» l'espace de noms à nouveau. Vous pouvez le définir dans l'espace de noms global (ou dans tout espace de noms englobant votre Namespace) comme

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

Puisque vous faites référence à une entité qui a déjà été déclarée dans l'espace de noms Namespace, vous pouvez utiliser un nom qualifié Namespace::Class.

Fourmi
la source
10
@STingRaySC: La seule façon de déclarer en avant une classe imbriquée est de placer la déclaration dans la définition de la classe englobante. Et il n'y a en effet aucun moyen de déclarer en avant la classe imbriquée avant la définition de la classe englobante.
Du
@STingRaySC: Une classe imbriquée peut être déclarée fwd - voir ma réponse.
John Dibling
8
@John Dibling: La classe imbriquée est une classe déclarée dans une autre classe. Une classe déclarée immédiatement dans un espace de noms n'est pas une classe imbriquée. Il n'y a rien à propos des classes sensted dans votre réponse.
Du
198

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

class Namespace::Class;

Pourquoi dois-je faire ça?

Vous devez le faire car le terme Namespace::Classindique au compilateur:

... OK, compilateur. Allez trouver l'espace de noms nommé Namespace, et dans celui-ci, faites référence à la classe nommée Class.

Mais le compilateur ne sait pas de quoi vous parlez car il ne connaît aucun espace de noms 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 depuis l'extérieur de cet espace de noms. Vous devez être dans l'espace de noms.

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

namespace Namespace
{
    class Class;
};
John Dibling
la source
39
Toutes les autres réponses étaient déroutantes pour moi, mais ceci "vous ne pouvez pas déclarer une classe dans un espace de noms depuis l'extérieur de cet espace de noms. Vous devez être dans l'espace de noms." était un indice très utile à retenir.
dashesy le
22

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

namespace Company::Communications::Sockets {
}

et vous devez faire ceci:

namespace Company {
  namespace Communications {
    namespace Sockets {
    }
  }
}
Igor Zevaka
la source
1
Ce n'est pas vraiment une réponse expliquant pourquoi vous ne pouvez pas le faire.
StarPilot
6
C'est une réponse qui m'a fait gagner beaucoup de temps
Kadir Erdem Demir
17
C ++ 17 ajoute ceci.
rparolin
Ici, vous savez que tous sont des espaces de noms. Mais avec la classe Company :: Communications :: Socket, vous ne savez pas si Communications est un espace de noms ou une classe (où socket est la classe imbriquée).
Lothar
12

Il ne serait pas clair quel est réellement le type d'une variable déclarée avant. La déclaration anticipée class Namespace::Class;pourrait signifier

namespace Namespace {
  class Class;
}

ou

class Namespace {
public:
  class Class;
};
Martin G
la source
6
Je pense que c'est l'une des meilleures réponses, car cela explique pourquoi cela ne peut pas être facilement déterminé par le compilateur lui-même.
Devolus
1

Il y a beaucoup d'excellentes réponses sur la justification de son refus. Je veux simplement fournir la disposition 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 constituée uniquement d' un identifiant de clé de classe ; est soit une redéclaration du nom dans la portée actuelle, soit une déclaration avant de l'identificateur en tant que nom de classe. Il introduit le nom de la classe dans la portée actuelle.

Ce qui précède définit ce qui constitue une déclaration avant (ou redclaration d'une classe). Essentiellement, il doit être l'un des class identifier;, struct identifier;ou union identifier;identifer est la définition lexicale courante 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

Quelle est la production du schéma commun que [a-zA-Z_][a-zA-Z0-9_]*nous connaissons tous. Comme vous pouvez le voir, cela empêche class foo::bar;d'être une déclaration avant valide, car ce foo::barn'est pas un identificateur. C'est un nom pleinement qualifié, quelque chose de différent.

Conteur - Unslander Monica
la source