Warum kann ich eine Klasse in einem Namespace nicht mit Doppelpunkten vorwärts deklarieren?

164
class Namespace::Class;

Warum muss ich das tun?:

namespace Namespace {
    class Class;
}

Unter Verwendung von VC ++ 8.0 gibt der Compiler folgende Probleme aus:

Fehler C2653: 'Namespace': ist kein Klassen- oder Namespace-Name

Ich gehe davon aus, dass das Problem hier darin besteht, dass der Compiler nicht erkennen kann, ob Namespacees sich um eine Klasse oder einen Namespace handelt. Aber warum ist das wichtig, da es sich nur um eine Vorwärtserklärung handelt?

Gibt es eine andere Möglichkeit, eine in einem Namespace definierte Klasse vorwärts zu deklarieren? Die obige Syntax fühlt sich so an, als würde ich den Namespace "wieder öffnen" und seine Definition erweitern. Was wäre, wenn Classnicht tatsächlich in definiert wären Namespace? Würde dies irgendwann zu einem Fehler führen?

Yong Li
quelle
44
Lassen Sie mich mit allen Antworten hier nicht einverstanden sein und sagen, dass es sich lediglich um einen Designfehler der Sprache handelt. Sie könnten besser denken.
Pavel Radzivilovsky
Dies führt zu einer Diskussion darüber, warum dies in C ++ illegal ist (was subjektiv ist), und es sieht argumentativ aus. Abstimmung zum Schließen.
David Thornley
7
Woher soll der Compiler wissen, dass in A::Bder Aeine Namespace-ID anstelle eines Klassennamens steht?
David R Tribble
@STingRaySC: Die Diskussion ist subjektiv, da es keine klare Antwort gibt, warum C ++ dies tut, also spekulieren wir. (Die Frage ist eine Schrotflintenfrage mit einigen Fragen mit objektiven Antworten, die bereits beantwortet wurden.) An diesem Punkt werde ich empfindlich gegenüber Argumentationsspuren, und Ihre Zustimmung zu Pavel, dass dies eine Fehlfunktion von C ++ ist, qualifiziert. Ich hätte kein Problem mit der Frage, warum es wichtig ist, ob Namespacees sich um eine Klasse oder einen Namespace handelt. Kommen Sie nur nicht an den Hinweis heran, dass möglicherweise ein Sprachflammenkrieg um die Syntax beginnen könnte.
David Thornley

Antworten:

85

Weil du nicht kannst. In der C ++ - Sprache werden vollständig qualifizierte Namen nur verwendet, um auf vorhandene (dh zuvor deklarierte) Entitäten zu verweisen . Sie können nicht zum Einführen neuer Entitäten verwendet werden.

Und Sie sind in der Tat des Namespace „Wiedereröffnung“ neue Einheiten zu erklären. Wenn die Klasse Classspäter als Mitglied eines anderen Namespace definiert wird, handelt es sich um eine völlig andere Klasse, die nichts mit der hier deklarierten zu tun hat.

Sobald Sie auf den Punkt zu bekommen definieren die vordeklarierte Klasse, brauchen Sie nicht zu „öffnen Sie “ wieder den Namespace. Sie können es im globalen Namespace (oder in einem beliebigen Namespace, der Ihren umschließt Namespace) als definieren

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

Da Sie sich auf eine Entität beziehen, die bereits im Namespace deklariert wurde Namespace, können Sie einen qualifizierten Namen verwenden Namespace::Class.

Ameise
quelle
10
@STingRaySC: Die einzige Möglichkeit, eine verschachtelte Klasse vorwärts zu deklarieren, besteht darin, die Deklaration in die Definition der einschließenden Klasse einzufügen . Und es gibt in der Tat keine Möglichkeit, die verschachtelte Klasse vor der Definition der einschließenden Klasse vorwärts zu deklarieren.
AnT
@STingRaySC: Eine verschachtelte Klasse kann vorwärts deklariert werden - siehe meine Antwort.
John Dibling
8
@ John Dibling: Verschachtelte Klasse ist eine Klasse, die in einer anderen Klasse deklariert ist. Eine Klasse, die unmittelbar in einem Namespace deklariert wird, ist keine verschachtelte Klasse. Ihre Antwort enthält nichts über sensted Klassen.
Am
198

Sie erhalten die richtigen Antworten. Lassen Sie mich einfach versuchen, die Formulierung neu zu formulieren:

class Namespace::Class;

Warum muss ich das tun?

Sie müssen dies tun, weil der Begriff Namespace::Classdem Compiler sagt:

... OK, Compiler. Suchen Sie den Namespace mit dem Namen Namespace und verweisen Sie darin auf die Klasse mit dem Namen Class.

Der Compiler weiß jedoch nicht, wovon Sie sprechen, da er keinen benannten Namespace kennt Namespace. Auch wenn es einen Namespace mit dem Namen gab Namespace, wie in:

namespace Namespace
{
};

class Namespace::Class;

Es würde immer noch nicht funktionieren, da Sie eine Klasse innerhalb eines Namespace nicht von außerhalb dieses Namespace deklarieren können. Sie müssen im Namespace sein.

Sie können also eine Klasse innerhalb eines Namespace deklarieren. Mach das einfach:

namespace Namespace
{
    class Class;
};
John Dibling
quelle
39
Alle anderen Antworten waren für mich verwirrend, aber dies "Sie können keine Klasse innerhalb eines Namespace von außerhalb dieses Namespace deklarieren. Sie müssen im Namespace sein." war ein sehr hilfreicher Hinweis, an den man sich erinnern sollte.
Bindestrich
22

Ich nehme an, aus dem gleichen Grund können Sie verschachtelte Namespaces nicht auf einmal wie folgt deklarieren:

namespace Company::Communications::Sockets {
}

und du musst das tun:

namespace Company {
  namespace Communications {
    namespace Sockets {
    }
  }
}
Igor Zevaka
quelle
1
Dies ist eigentlich keine Antwort, die erklärt, warum Sie dies nicht tun können.
StarPilot
6
Dies ist eine Antwort, die viel Zeit gespart hat
Kadir Erdem Demir
17
C ++ 17 fügt dies hinzu.
Rparolin
Hier wissen Sie, dass alle Namespaces sind. Bei der Klasse Company :: Communications :: Socket wissen Sie jedoch nicht, ob Communications ein Namespace oder eine Klasse ist (wobei Socket die verschachtelte Klasse ist).
Lothar
11

Es wäre nicht klar, was der Typ einer vorwärts deklarierten Variablen tatsächlich ist. Die Vorwärtserklärung class Namespace::Class;könnte bedeuten

namespace Namespace {
  class Class;
}

oder

class Namespace {
public:
  class Class;
};
Martin G.
quelle
6
Ich denke, dies ist eine der besten Antworten, weil sie antwortet, warum dies vom Compiler selbst nicht leicht festgestellt werden kann.
Devolus
1

Es gibt viele ausgezeichnete Antworten auf die Gründe, warum es nicht erlaubt ist. Ich möchte nur die langweilige Standardklausel bereitstellen, die dies ausdrücklich verbietet. Dies gilt für C ++ 17 (n4659).

Der fragliche Absatz lautet [class.name] / 2 :

Eine Deklaration, die ausschließlich aus einer Klassenschlüsselkennung besteht . ist entweder eine erneute Deklaration des Namens im aktuellen Bereich oder eine Vorwärtsdeklaration des Bezeichners als Klassenname. Es führt den Klassennamen in den aktuellen Bereich ein.

Das Obige definiert, was eine Vorwärtsdeklaration (oder Redclaration einer Klasse) darstellt. Im Wesentlichen muss es einer der folgenden sein, class identifier;, struct identifier;oder union identifier;wo identifer ist die gemeinsame lexikalische Definition in [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

Welches ist die Produktion des gemeinsamen Schemas, mit dem [a-zA-Z_][a-zA-Z0-9_]*wir alle vertraut sind. Wie Sie sehen können, ist dies keine class foo::bar;gültige Vorwärtsdeklaration, da foo::bares sich nicht um eine Kennung handelt. Es ist ein voll qualifizierter Name, etwas anderes.

Geschichtenerzähler - Unslander Monica
quelle