Ratschläge zu C ++ - Namespaces

75

Ich unterrichte mich nur selbst in C ++ - Namespaces (mit C # -Hintergrund) und beginne wirklich zu denken, dass verschachtelte Namespaces trotz all der Dinge, die C ++ besser macht als die meisten anderen Sprachen, nicht dazu gehören!

Habe ich Recht, wenn ich denke, dass ich Folgendes tun muss, um einige verschachtelte Namespaces zu deklarieren:

namespace tier1
{
    namespace tier2
    {
        namespace tier3
        {
            /* then start your normal code nesting */
        }
    }
}

Im Gegensatz zu:

namespace tier1::tier2::tier3
{
}

à la C #?

Dies wird noch wahnsinniger, wenn ich weiterleiten muss:

namespace tier1
{
    namespace tier2
    {
        namespace forward_declared_namespace
        {
            myType myVar; // forward declare
        }
        namespace tier3
        {
            /* then start your normal code nesting */
            class myClass
            {
                forward_declared_namespace::myType myMember;
            }
        }
    }
}

In Anbetracht dessen, dass ein typisches System, das ich entwickle, besteht aus:

MyCompany::MySolution::MyProject::System::[PossibleSections]::Type

Ist dies der Grund, warum Namespaces in C ++ - Beispielen nicht häufig verwendet werden? Oder normalerweise nur einzelne (nicht verschachtelte) Namespaces?

AKTUALISIEREN

Für alle Interessierten ist dies der Grund , warum ich dieses Problem angegangen bin .

Adam Naylor
quelle
8
Schließlich wird C ++ 17 erlauben namespace tier1::tier2::tier3.
underscore_d
7
Der Link im Update ist defekt ...
AlwaysLearning
2
Ich war daran interessiert zu erfahren, wie Sie das Problem gelöst und auf Ihren Link geklickt haben. Leider wurde, wie AlwaysLearning bereits betont hat, die Verbindung unterbrochen.
CSJ

Antworten:

112

C ++ - Namespaces waren nicht als Entwurfsmechanismus gedacht - sie dienen lediglich dazu, Namenskonflikte zu verhindern. In 99,99% der Situationen möchten oder müssen Sie wirklich keine verschachtelten Namespaces verwenden.

Ein gutes Beispiel für die korrekte Verwendung von Namespaces in C ++ ist die C ++ - Standardbibliothek. Alles in dieser ziemlich großen Bibliothek befindet sich in einem einzigen Namespace namens std Es besteht kein Versuch oder Bedarf, die Bibliothek in (zum Beispiel) einen E / A-Sub-Namespace, einen mathematischen Sub-Namespace oder einen Container-Sub-Namespace aufzuteilen etc.

Das grundlegende Werkzeug für die Modellierung in C ++ ist die Klasse (und in gewissem Maße die Vorlage), nicht der Namespace. Wenn Sie eine Verschachtelung benötigen, sollten Sie verschachtelte Klassen verwenden, die gegenüber Namespaces die folgenden Vorteile bieten:

  • Sie haben Methoden
  • Sie können den Zugriff kontrollieren
  • Sie können nicht wieder geöffnet werden

Wenn Sie diese berücksichtigt haben und dennoch verschachtelte Namespaces verwenden möchten, ist dies technisch gesehen nichts Falsches daran, sie auf diese Weise zu verwenden.


quelle
18
Vorlagen waren nicht als MPL-Mechanismus gedacht. Aber Jungs benutzen sie so.
Mykola Golubyev
13
Eh. . . Ich hasse es, ein Pedant zu sein (wir alle wissen, dass das eine Lüge ist), aber implementiert <iostream> nicht einen ios-Namespace im std-Namespace? Ich habe momentan keinen C ++ - Compiler zur Hand. . .
Binary Worrier
1
Könnten Sie Argumente (keine Beispiele) angeben, warum ist es nicht ratsam, verschachtelte Namesapces zu verwenden?
Bayda
4
@binary ios ist ein typedef für basic_ios <char>
7
Namespaces in C ++ verfügen über spezielle Funktionen, um Schnittstellen modularer zu unterstützen. Bitte beachten Sie die folgenden Regeln im hervorragenden Buch "C ++ Coding Standards" von Herb Sutter und Andrei Alexandrescu: 57: Halten Sie einen Typ und seine Nichtmitgliedsfunktionsschnittstelle im selben Namespace. 58: Bewahren Sie Typen und Funktionen in separaten Namespaces auf, es sei denn, sie sollen speziell zusammenarbeiten.
Alexk7
23

C ++ - Namespaces waren eine enorme Verbesserung gegenüber dem vorherigen Angebot (dh überhaupt keine Namespaces). C # -Namensräume haben das Konzept erweitert und sind damit gelaufen. Ich würde Ihnen raten, Ihre Namespaces in einer einfachen flachen Struktur zu halten.

EDIT Do Sie darauf hinweisen , dass aufgrund der Unzulänglichkeiten ich hier skizziert habe?

Einfach "Ja". C ++ - Namespaces wurden nicht entwickelt, um Ihnen zu helfen, Ihre Logik und Bibliotheken so zu partitionieren, wie sie es in C # tun.

Der Zweck von C ++ - Namespaces besteht darin, das Problem der realen Welt zu stoppen, auf das C-Entwickler stoßen, bei dem Namenskollisionen auftreten, wenn zwei Bibliotheken von Drittanbietern verwendet werden, die dieselben Funktionsnamen exportieren. C-Entwickler hatten verschiedene Problemumgehungen, aber es könnte ein ernsthafter Schmerz sein.

Die Idee war, dass die STL usw. den std::Namespace hat, die von "XYZ Corp" bereitgestellten Bibliotheken einen xyz::Namespace haben würden, wenn Sie für "ABC corp" arbeiten würden, würden Sie alle Ihre Sachen in einem einzigen abc::Namespace zusammenfassen.

Binärer Worrier
quelle
2
Raten Sie das aufgrund der hier beschriebenen Mängel?
Adam Naylor
Wenn viele Leute bei "ABC corp" für viele Projekte dort arbeiten und im Laufe der Jahre Projekte zusammengeführt oder aufgeteilt werden, ist es sehr sinnvoll, mindestens abc::PROJECTSub-Namespaces zu haben. Und wenn größere Projekte klare Abschnitte haben, ist das abc::PROJECT::SECTIONauch sinnvoll. Sogar die C ++ - Götter haben verstanden, dass es Abschnitte gibt, und Sub-Namespaces wie std::chronooder erstellt std::ios.
Kai Petzke
19

Was ich mache, wenn ich vorwärts deklariere, sieht so aus:

 namespace abc { namespace sub { namespace subsub { class MyClass; }}}

Meine Vorwärtserklärungen sind in einer Zeile zusammengefasst. Die Lesbarkeit der Weiterleitungsdeklaration wird im Gegenzug für die Lesbarkeit des restlichen Codes geopfert. Und für Definitionen verwende ich auch keine Einrückungen:

 namespace abc {
 namespace sub {
 namespace subsub {
 
 class MyClass 
 {
    public:
       MyClass();

       void normalIntendationsHere() const;
 };

 }
 }
 }

Die Verwendung dieses Stils erfordert am Anfang ein wenig Disziplin, ist aber der beste Kompromiss für mich.


Seit C ++ 17 können Sie den Namespace mit einer vom Autor der Frage vorgeschlagenen Syntax deklarieren.

namespace A::B::C { ... }

verschachtelte Namespace-Definition: namespace A::B::C { ... }entspricht namespace A { namespace B { namespace C { ... } } }.

https://en.cppreference.com/w/cpp/language/namespace

doc
quelle
7
Dies ist keine Antwort auf die gestellte Frage.
Paul Merrill
2
@Jammer: Ich war damals neu im Stackoverflow und hatte keine Idee :) Sry.
Doc
8

Zumindest als kleine Hilfe können Sie in einigen Fällen Folgendes tun:

namespace foo = A::B::C::D;

Und dann A :: B :: C :: D als foo bezeichnen. Aber nur in einigen Fällen.

Bill Lynch
quelle
2
In welchen Fällen können Sie das nicht tun?
Michael Dorst
Der Fall in der Frage ist einer. Ich denke?
Leichtigkeitsrennen im Orbit
6

Sie können Einrückungen überspringen. Ich schreibe oft

namespace myLib { namespace details {

/* source code */

} } /* myLib::details */

C ++ - Quellcode wird schließlich in Binärform kompiliert, im Gegensatz zu C # / Java, das in der Binärdatei verbleibt. Daher bietet der Namespace nur eine gute Lösung für den Konflikt bei der Benennung von Variablen. Es ist nicht für die Klassenhierarchie vorgesehen.

Ich behalte oft ein oder zwei Namespace-Ebenen im Code.

süße Katze
quelle
4

Zunächst können Sie das Einrücken von Namespaces vermeiden, da es dafür keinen Grund gibt.

Die Verwendung von Namespaces in den Beispielen zeigt nicht die Leistung von Namespaces an. Und ihre Macht für mich besteht darin, Domain-Bereiche voneinander zu trennen. Teilen Sie Utility-Klassen von den Business-Klassen.

Mischen Sie nur keine unterschiedlichen Namespace-Hierarchien in der einen .h-Datei. Namespaces sind eine Art zusätzlicher Kommentar für Ihre Schnittstelle der Funktionsdeklaration. Das Betrachten von Namespaces und Klassennamen sollte eine Menge Dinge erklären.

namespace product
{
namespace DAO
{

class Entity
{
};
Mykola Golubyev
quelle
3
"Zunächst einmal können Sie das Einrücken von Namespaces vermeiden, da es dafür keinen Grund gibt." genau das mache ich eigentlich;)
Adam Naylor
Ein großartiger Punkt, und ich denke, dass die Leistung von Namespaces weiter verbessert wird, wenn CASE- Tools zur Visualisierung von Codebasisbeziehungen und -ebenen verwendet werden. Tools wie CppDepend oder Visual Studios Dependency Graph verwenden solche vom Entwickler erstellten "Code-Verständlichkeitsgriffe", die über alle Namespace-Verschachtelungen strukturiert sind, um die Ebenen und Abhängigkeiten auf eine gut strukturierte, kohärente Weise wirklich zu beleuchten.
Jxramos
0

Sie überbeanspruchen sie (und Sie erhalten nichts zurück).

Jimmy J.
quelle
15
Eine hierarchische, geordnete Struktur. (bezüglich "Sie erhalten nichts zurück") Ich denke, das ist riesig und in C ++ stark unterschätzt. Denken Sie nur daran, wie sich dies auf die Dokumentation auswirkt.
Konrad Rudolph
1
@Konrad, ich glaube, das ist die Essenz meiner Frage.
Adam Naylor
Die Frage ist sehr alt, aber für mich heute noch relevant. Ich hatte die gleiche Meinung wie @KonradRudolph, aber dann fand ich heraus, dass verschachtelte Namespaces wertlos sind . Der beste Weg ist, nur einen flachen, breiten Bibliotheks-Namespace zu haben ... Es ist traurig, aber es ist das Beste, was wir gerade haben. Und das folgt mit der Struktur von std. Und es ist aus einem bestimmten Grund so strukturiert - es gibt derzeit keinen besseren Weg.
Sahsahae
@Sahsahae Sie sind definitiv nicht wertlos, auch nicht nach Meinung (!) Eines hochrangigen Mitglieds des C ++ - Standardkomitees. Aber selbst dann ist es nur das: eine Meinung (die ich nicht teile). Ich empfehle definitiv nicht, leichtfertig zu verschachteln - aber die vernünftige Verwendung verschachtelter Namespaces wird in anderen Sprachen häufig und erfolgreich eingesetzt und weist nicht die von Titus postulierten Mängel auf.
Konrad Rudolph
@KonradRudolph Hier ist Ihr Problem: andere Sprachen. Bei dieser Frage ging es ausschließlich um C ++ - Namespaces, und ich habe den Umfang nicht erweitert. Lustigerweise habe ich es aus Versehen, genau wie bei C ++ - Namespaces, irgendwie geschafft, es jetzt auf "Namespace in allen Sprachen" zu erweitern, weil langs::cppich immer noch alles sehen kann langs, ohne wirklich isoliert zu sein cpp... Dies ist genau das Problem mit C ++ - Namespaces und warum flache Struktur wird vorgeschlagen. in Rust Module, zum Beispiel, könnten Sie nicht Zugang langs::*von cpp, und ich habe nie gesagt , dass dies schlecht ist, ist es großartig, aber in C ++ ist es nicht so.
Sahsahae
0

Ich habe festgestellt, dass Sie den c # -Namensraum so nachahmen können.

namespace ABC_Maths{
    class POINT2{};
    class Complex{};
}

namespace ABC_Maths_Conversion{
    ABC_MATHS::Complex ComplexFromPOINT2(ABC_MATHS::POINT2)
    {return new ABC_MATHS::Complex();}

    ABC_MATHS::POINT4 POINT2FromComplex(ABC_MATHS::COMPLEX)
    {return new ABC_MATHS::POINT2();}
}

namespace ABC
{
}

Aber Code scheint nicht sehr ordentlich zu sein. und ich würde erwarten, langwierigen Gebrauch zu haben

Es ist besser, so viel Funktionalität wie möglich in Klassen zu verschachteln

namespace ABC{
    class Maths{
        public:
        class POINT2{};
        class Complex:POINT2{};
        class Conversion{
            public:
            static Maths.Complex ComplexFromPOINT2(MATHS.POINT2 p)
            {return new MATHS.Complex();}

            static MATHS.POINT2 POINT2FromComplex(MATHS.COMPLEX p)
            {return new ABC::MATHS.POINT2();}// Can reference via the namespace if needed
} /*end ABC namespace*/

Und das ist noch ein bisschen langwierig. fühlt sich aber ein bisschen OO an.

Und hört, wie es am besten gemacht scheint

namespace ABC
{
    class POINT2{};
    class Complex:POINT2{};
    Complex ComplexFromPOINT2(POINT2 p){return new Complex();}
    POINT2 POINT2FromComplex(Complex){return new POINT2();}
}

hört, wie Verwendungen aussehen würden

int main()
{
    ABC_Maths::Complex p = ABC_Maths_Conversion::ComplexFromPOINT2(new ABC_MATHS::POINT2());

    // or THE CLASS WAY

    ABC.Maths.Complex p = ABC.Maths.Conversion.ComplexFromPOINT2(new ABC.Maths.POINT2());

    // or if in/using the ABC namespace

    Maths.Complex p = Maths.Conversion.ComplexFromPOINT2(new Maths.POINT2());

    // and in the final case

    ABC::Complex p = ABC::ComplexFromPOINT2(new ABC::POINT2());
}

Es war interessant herauszufinden, warum ich nie C ++ - Namespaces verwendet habe, wie ich es mit c # tun würde. Es wäre zu langwierig und würde niemals so funktionieren wie c # -Namensräume.

Die beste Verwendung für Namespaces in C ++ besteht darin, zu verhindern, dass meine Super-Cout-Funktion (die jedes Mal klingelt, wenn sie aufgerufen wird) mit der Funktion std :: cout verwechselt wird (was weitaus weniger beeindruckend ist).

Nur weil c # und c ++ Namespaces haben, bedeutet dies nicht, dass Namespace dasselbe bedeutet. Sie sind unterschiedlich, aber ähnlich. Die Idee für c # -Namensräume muss aus c ++ - Namespaces stammen. Jemand muss gesehen haben, was eine ähnliche, aber andere Sache tun könnte, und hatte nicht mehr genug Vorstellungskraft, um ihm seinen eigenen ursprünglichen Namen wie "ClassPath" zu geben, was eher sinnvoll wäre, als ein Weg zum Unterricht zu sein, als ihn bereitzustellen Benennen von Räumen, in denen jeder Raum den gleichen Namen haben kann

Ich hoffe das hilft jemandem

Ich habe vergessen zu sagen, dass all diese Möglichkeiten gültig sind und eine moderate Verwendung der ersten beiden in die dritte integriert werden kann, um eine Bibliothek zu erstellen, die Sinn macht, dh (kein gutes Beispiel)

_INT::POINT2{}

und

_DOUBLE::POINT2{}

So können Sie die Genauigkeitsstufe mithilfe von ändern

#define PREC _DOUBLE 
// or #define PREC _INT 

und dann Erstellen einer Instanz von PREC :: POINT2 für POINT2 mit doppelter Genauigkeit

Mit C # - oder Java-Namespaces ist das nicht einfach

das ist natürlich nur ein beispiel. Überlegen Sie, wie die Verwendung gelesen wird.

Ace_Of_John
quelle
-1

Manchmal deklariere ich tiefe Namespaces als ein paar Makros in einem separaten Header

Namespace.h

#define NAMESPACE_TIER1_TIER2_TIER3 \
    namespace tier1 { \
    namespace tier2 { \
    namespace tier3 {

#define END_NAMESPACE_TIER1_TIER2_TIER3 }}}

Um es woanders zu verwenden:

anotherfile.h

#include "./namespace.h"

NAMESPACE_TIER1_TIER2_TIER3;

/* Code here */

END_NAMESPACE_TIER1_TIER2_TIER3;

Die redundanten Semikolons nach dem Makro sollen zusätzliche Einrückungen vermeiden.

Eis
quelle
2
Das ist wirklich chaotisch und Sie müssten es für jeden verschachtelten Namespace definieren. Plus, wenn Sie schreiben wollen NAMESPACE_TIER1_TIER2_TIER3und END_NAMESPACE_TEIR1_TIER2_TIER3warum nicht einfach schreiben namespace tier1{namespace tier2{namespace tier3{und }}}? Insgesamt ist es tatsächlich kürzer.
Michael Dorst
Ich bin anderer Meinung, es ist nicht kürzer und es entsteht ein sehr tiefes Einrückungsproblem. Ich habe beide ausprobiert und bevorzuge die Methode #define. Bearbeiten: ok, abzüglich des Einrückungsproblems ...
Eis
1
Bevorzugen Sie, was Ihnen gefällt, aber ich würde das anderen Benutzern nicht empfehlen. Es ist nicht kürzer (wie Sie aus meinem Kommentar sehen können, wo die beiden in einer Reihe stehen) und das zählt nicht die Definition, und es ist sicherlich schwieriger, Fehler auf diese Weise zu verfolgen. Was ist, wenn Sie einen Tippfehler machen? Der Compiler weiß nicht, was falsch ist, und Ihr Fehler wird an der völlig falschen Stelle angezeigt.
Michael Dorst
3
Das "kürzere" Argument gilt nicht wirklich, da es von der Art und Weise abhängt, wie die Makros definiert sind. Und über das "Tippfehler" -Argument. Wenn Sie den falschen Namespace-Namen eingeben, erhalten Sie genauso kryptische Nachrichten wie bei der Makrolösung. Bei der Makrolösung können jedoch viele moderne IDEs beheben, dass der Name nicht definiert wurde, und Sie können den Fehler leichter lokalisieren. Der Namespace-Name ist nur Freitext und Sie können dort alles schreiben, was Sie wollen. Daher ist es tatsächlich schwieriger, Tippfehler zu vermeiden, wenn Sie das Makro nicht verwenden. Und es schadet nicht, die Makrolösung zu "empfehlen", die Menschen können selbst entscheiden.
Eis