webentwicklung-frage-antwort-db.com.de

Warum können wir keinen Namespace innerhalb einer Klasse deklarieren?

Das Deklarieren einer Klasse innerhalb einer Klasse ist gültig. (Verschachtelte Klassen)

Das Deklarieren eines Namespaces innerhalb einer Klasse ist ungültig.

Die Frage ist: Gibt es einen guten Grund (abgesehen von C++ - Grammatik-/Syntaxproblemen), die Deklaration eines Namespaces innerhalb einer Klasse zu verbieten?


Warum sollte ich das tun? Hier ein Beispiel:

Lassen Sie uns eine grundlegende Beschreibung eines binären Baumcontainers haben

template<typename Data>
class binary_tree
{
 public:
  ... stuff ....     

 private:
  ... iterators class declaration ...

 public:
  typedef left_depth_iterator_impl     left_depth_iterator;
  typedef right_depth_iterator_impl    right_depth_iterator;
  typedef left_breadth_iterator_impl   left_breadth_iterator;
  typedef right_breadth_iterator_impl  right_breadth_iterator;

  ... stuff ....     

 private:
  Data         data;
  binary_tree* left;
  binary_tree* right;
};

Jetzt stelle ich fest, dass es viele Iteratoren in meiner Klasse gibt, daher möchte ich sie innerhalb desselben Namensraums wie folgt umgruppieren:

template<typename Data>
class binary_tree
{
 public:
  ... stuff ....     

 private:
  ... iterators class declaration ...

 public:
  namespace iterator
  {
    typedef left_depth_iterator_impl     left_depth;
    typedef right_depth_iterator_impl    right_depth;
    typedef left_breadth_iterator_impl   left_breadth;
    typedef right_breadth_iterator_impl  right_breadth;
  }

  ... stuff ....     

 private:
  Data         data;
  binary_tree* left;
  binary_tree* right;
};

Dies würde eine einfache Verwendung ermöglichen:

void  function()
{
  binary_tree::iterator::left_depth   it;

  ...stuff...
}

Dies funktioniert, wenn ich eine Klasse anstelle eines Namespaces verwende, aber dann bin ich dazu gezwungen, eine Klasse zu deklarieren, die nie instanziiert wird, was ein recht großer Namespace ist.

Warum erlaube ich verschachtelte Klassen und verbiete verschachtelte Namespaces innerhalb von Klassen? ist es eine alte Last?


Antworten aus semantischen Gründen, die nicht nur einen Teil des Standards (insbesondere Syntaxteile) enthalten, werden geschätzt :)

40
Drax

Das Hinzufügen einer solchen Funktion zur Sprache ist nicht wirklich von Vorteil. Features werden im Allgemeinen nicht hinzugefügt, wenn keine Nachfrage besteht.

Was würden Sie mit Namespaces in Klassen kaufen? Möchten Sie wirklich lieber binary_tree::iterator::left_depth sagen als einfach binary_tree::left_depth? Wenn Sie mehrere Namespaces in sich hatten, können Sie sie beispielsweise zur Unterscheidung von binary_tree::depth_iterator::left und binary_tree::breadth_iterator::right verwenden.

In jedem Fall können Sie das gewünschte Ergebnis erzielen, indem Sie interne Klassen als Namespace eines Programmierers für schlechte Programmierer verwenden. Dies ist ein noch mehr Grund, warum in Klassen kein Bedarf an echten Namespaces besteht.

8
Adrian McCarthy

Da Sie gefragt haben, welche Teile des Standard-Mandats-Namespace-Speicherorts vorhanden sind, schlagen wir das zuerst an:

C++ 11 7.3-p4: Jede Namensraumdefinition soll im globalen Bereich oder in einem Namensraumbereich (3.3.6) erscheinen.

In Bezug auf Klassendefinitionen und den Vorschlag, einen Namensraum innerhalb zu deklarieren, bringe ich Sie zum ...

C++ 11 9.2-p2: Eine Klasse wird beim Schließen} des Klassenbezeichners als vollständig definierter Objekttyp (3.9) (oder vollständiger Typ) betrachtet. Innerhalb der Klassenmitgliedsspezifikation wird die Klasse als vollständig innerhalb von Funktionskörpern, Standardargumenten, Ausnahmespezifikationen und Anführungszeichen für geschweifte Klammern oder gleiche für nicht statische Datenelemente (einschließlich solcher Elemente in verschachtelten Klassen) betrachtet. Andernfalls wird es innerhalb seiner eigenen Klassenmitgliedervorgabe als unvollständig betrachtet.

Ergo ist eine Klassendefinition endlich, sobald der Schlusslocken erreicht ist. Es kann nicht wieder geöffnet und erweitert werden (Ableitung ist etwas anderes, erweitert jedoch NICHT die gerade definierte Klasse).

Aber am Anfang der Standarddefinition eines Namespaces lauert die Fähigkeit, ihn zu erweitern. um es aus Mangel an einem besseren Begriff zu erweitern:

C++ 7.3-p1: Ein Namespace ist eine deklarative Region mit optionalem Namen. Der Name eines Namespaces kann für den Zugriff auf Entitäten verwendet werden, die in diesem Namespace deklariert sind. das sind die Mitglieder des Namespaces. Im Gegensatz zu anderen deklarativen Regionen kann die Definition eines Namespaces auf mehrere Teile einer oder mehrerer Übersetzungseinheiten aufgeteilt werden. (Betonung hinzugefügt).

Daher würde ein Namespace innerhalb einer Klasse die Definition in 7.3-p4 verletzen. Angenommen, es sei nicht vorhanden, wäre es möglich, einen Namespace an einer beliebigen Stelle zu deklarieren, auch in einer Klasse. Da die Definition einer Klasse nach dem Schließen jedoch formalisiert ist, bleibt nur die Möglichkeit, dies zu tun Gehen Sie folgendermaßen vor, wenn Sie die Einhaltung von 7.3-p1 aufrechterhalten:

class Foo
{
   namespace bar
   {
       ..stuff..
   }

   .. more stuff ..

   namespace bar
   {
       ..still more stuff..
   }
};

Die Nützlichkeit dieses -Features wurde wahrscheinlich für etwa 3 volle Sekunden diskutiert, bevor 7.3-p4 eingerichtet wurde.

42
WhozCraig

Ich werde anderen hier nicht zustimmen. Ich würde nicht sagen, dass es keinen wirklichen Vorteil gibt. Manchmal möchte ich einfach Code ohne zusätzliche Auswirkungen trennen. Als Beispiel arbeitete ich in einem Multithreading-Ringpuffermodul und wollte die Statusmitglieder, von denen einige atomar und/oder speicherorientiert sind, in Namespaces für den Produzenten und den Konsumenten aufteilen.

Indem ich einfach alles mit den Präfixen producer oder consumer benenne (dies ist meine derzeit ärgerliche Implementierung), füge ich eine Verschmutzung hinzu, die den Code schwerer lesbar macht. Z.B. Wenn alles, was sich im Besitz des Produzenten befindet, mit producer beginnt, ist es für Ihr Gehirn einfacher, versehentlich eine automatische Korrektur von producerProducerTimer (Produzentenkopie eines Produzententimers) als producerConsumerTimer (Erzeugerschatten eines Verbrauchertimers) oder consumerProducerTimer (Verbraucherschatten eines Produzententimers) vorzunehmen. . Das Debuggen dauert viel länger als nötig, da der Code nicht mehr skalierbar ist.

Erstellen Sie eine verschachtelte Klasse/Struktur:

  • Ich könnte dem nächsten Entwickler, der diesen Code unterhält, die Idee geben, dass mehr als einer von ihnen in einem Kontext instanziiert, kopiert und einander zugewiesen werden könnte/muss. Daher muss ich jetzt, anstatt mir nur um die Namensgebung zu sorgen, diese = delete-Anweisungen geben Dinge.
  • Ich könnte den Footprint mit struktureller Ausrichtungsauffüllung hinzufügen, die sonst möglicherweise nicht erforderlich ist.
  • Alle Mitglieder als statisch zu definieren ist keine Option, da mehr als ein Kontext instanziiert werden kann, für den seine eigenen Erzeuger-/Konsumzustandsvariablen benötigt werden.
  • Funktionen einer solchen Struktur haben keinen Zugriff mehr auf andere Mitgliedsdaten oder Funktionen, z. B. Konstanten oder Funktionen, die von beiden Seiten gemeinsam genutzt werden, sondern müssen diese Dinge als Argumente verwenden.

Im Idealfall möchte ich so etwas ändern können:

rbptr producerPosition;
rbptr consumerPosition;

zu diesem:

namespace producer
{
    rbptr position;
}
namespace consumer
{
    rbptr position;
}

Dann können Funktionen, die nur Consumer-Mitglieder berühren sollen, den Consumer-Namespace verwenden, Funktionen, die nur die Produzent-Mitglieder berühren sollen, können den Produzenten-Namespace verwenden, und Funktionen, die beide berühren müssen, müssen diese explizit qualifizieren. Es gibt keine Möglichkeit, versehentlich eine Consumer-Variable in einer Funktion zu berühren, die nur den Produzenten-Namespace verwendet.

In diesem Fall besteht der Wunsch lediglich darin, Namenskollisionen zwischen Produzenten- und Konsumentenkopien von Dingen zu reduzieren, und Namenskollisionen sind die, für die Namensräume existieren. Aus diesem Grund unterstütze ich den Vorschlag, Namensräume innerhalb von Klassen deklarieren zu können.

11
ErsatzStoat

Dies ist einfach nicht der Punkt von Namespaces. Namensräume sollen näher an der obersten Codeebene existieren, so dass zwei verschiedene Unternehmen (oder Codebasen) Code miteinander mischen können. Auf einer Mikroebene codiere ich sowohl mit IMAP für den E-Mail-Zugriff als auch mit SMTP für das Senden von E-Mails und habe (könnte ich vereinfacht werden) Klassen in beiden Modulen mit dem Namen Email, die sehr unterschiedlich sind, aber ich könnte eine Anwendung haben, beispielsweise eine E-Mail Client, der beide aus derselben Klasse verwenden möchte, z Vielleicht leitet es Mails von einem Konto an ein anderes weiter. Namensräume/Paketnamen/etc. erlauben dies.

Was Sie vorgeschlagen haben, ist nicht einfach, wofür Namespaces gedacht sind. Innerhalb einer Datei können Sie verschiedene Namen vergeben, da der Autor über umfassende Kenntnisse der Datei verfügt. Dies ist jedoch nicht der Fall, wenn zwei Unternehmen Code oder zwei Anwendungen gemeinsam nutzen möchten das wusste nicht, dass sie irgendwann kollidieren würden.

4
djechlin

Nur ein kleiner Gedanke, den ich für erwähnenswert halte. Eine Verwendung von Namespaces innerhalb von Klassen wäre ein funktionales Äquivalent zu templatierten Namespaces.

template<class...types>
struct Namespace {
    namespace Implementation {
        ...
    }
};

// somewhere else

using namespace Namespace<types...>::Implementation;

// use templated stuff.

Ich persönlich würde diese Funktion genießen, aber es scheint, dass die Nachfrage nicht hoch genug ist, um sie umzusetzen.

0