Warum werden unbenannte Namespaces verwendet und welche Vorteile haben sie?

242

Ich bin gerade einem neuen C ++ - Softwareprojekt beigetreten und versuche, das Design zu verstehen. Das Projekt verwendet häufig unbenannte Namespaces. So etwas kann beispielsweise in einer Klassendefinitionsdatei vorkommen:

// newusertype.cc
namespace {
  const int SIZE_OF_ARRAY_X;
  const int SIZE_OF_ARRAY_Y;
  bool getState(userType*,otherUserType*);
}

newusertype::newusertype(...) {...

Welche Entwurfsüberlegungen können dazu führen, dass ein unbenannter Namespace verwendet wird? Was sind die Vor- und Nachteile?

Scottie T.
quelle

Antworten:

189

Unbenannte Namespaces sind ein Dienstprogramm, um eine Bezeichner-Übersetzungseinheit lokal zu machen. Sie verhalten sich so, als würden Sie einen eindeutigen Namen pro Übersetzungseinheit für einen Namespace auswählen:

namespace unique { /* empty */ }
using namespace unique;
namespace unique { /* namespace body. stuff in here */ }

Der zusätzliche Schritt zur Verwendung des leeren Körpers ist wichtig, sodass Sie bereits innerhalb des Namespace-Körpers auf solche Bezeichner verweisen können ::name, die in diesem Namespace definiert sind, da die using-Direktive bereits stattgefunden hat.

Dies bedeutet, dass Sie kostenlose Funktionen haben können, die (zum Beispiel) aufgerufen werden und helpin mehreren Übersetzungseinheiten vorhanden sein können und die zum Zeitpunkt der Verknüpfung nicht zusammenstoßen. Der Effekt ist fast identisch mit der Verwendung des staticin C verwendeten Schlüsselworts, das Sie in die Deklaration der Bezeichner einfügen können. Unbenannte Namespaces sind eine überlegene Alternative, da sie sogar eine Typübersetzungseinheit lokalisieren können.

namespace { int a1; }
static int a2;

Beide asind lokale Übersetzungseinheiten und stoßen zur Verbindungszeit nicht zusammen. Der Unterschied besteht jedoch darin, dass der a1im anonymen Namespace einen eindeutigen Namen erhält.

Lesen Sie den ausgezeichneten Artikel bei comeau-computer. Warum wird ein unbenannter Namespace anstelle von statischem verwendet? ( Archive.org Spiegel ).

Johannes Schaub - litb
quelle
Sie erklären die Beziehung zu static. Kannst du bitte auch mit vergleichen __attribute__ ((visibility ("hidden")))?
Phinz
74

Wenn sich etwas in einem anonymen Namespace befindet, bedeutet dies, dass es für diese Übersetzungseinheit lokal ist (CPP-Datei und alle darin enthaltenen Elemente). Wenn ein anderes Symbol mit demselben Namen an anderer Stelle definiert wird, liegt kein Verstoß gegen die One Definition Rule (ODR) vor.

Dies entspricht der C-Methode für eine statische globale Variable oder statische Funktion, kann jedoch auch für Klassendefinitionen verwendet werden (und sollte nicht staticin C ++ verwendet werden).

Alle anonymen Namespaces in derselben Datei werden als der gleiche Namespace behandelt, und alle anonymen Namespaces in verschiedenen Dateien sind unterschiedlich. Ein anonymer Namespace entspricht:

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;
Motti
quelle
14

Der unbenannte Namespace beschränkt den Zugriff von Klassen, Variablen, Funktionen und Objekten auf die Datei, in der er definiert ist. Die Funktionalität für unbenannte Namespaces ähnelt dem staticSchlüsselwort in C / C ++.
staticDas Schlüsselwort beschränkt den Zugriff der globalen Variablen und Funktionen auf die Datei, in der sie definiert sind.
Es gibt einen Unterschied zwischen unbenanntem Namespace und staticSchlüsselwort, aufgrund dessen unbenannter Namespace gegenüber statischen einen Vorteil hat. staticDas Schlüsselwort kann mit Variablen, Funktionen und Objekten verwendet werden, jedoch nicht mit benutzerdefinierten Klassen.
Beispielsweise:

static int x;  // Correct 

Aber,

static class xyz {/*Body of class*/} //Wrong
static structure {/*Body of structure*/} //Wrong

Dies kann jedoch auch mit einem unbenannten Namespace möglich sein. Beispielsweise,

 namespace {
           class xyz {/*Body of class*/}
           static structure {/*Body of structure*/}
  } //Correct
Sachin
quelle
13

Neben den anderen Antworten auf diese Frage kann die Verwendung eines anonymen Namespace auch die Leistung verbessern. Da Symbole im Namespace keine externe Verknüpfung benötigen, kann der Compiler den Code im Namespace aggressiver optimieren. Beispielsweise kann eine Funktion, die mehrmals in einer Schleife aufgerufen wird, ohne Auswirkungen auf die Codegröße eingefügt werden.

Auf meinem System benötigt der folgende Code beispielsweise etwa 70% der Laufzeit, wenn der anonyme Namespace verwendet wird (x86-64 gcc-4.6.3 und -O2; beachten Sie, dass der Compiler durch den zusätzlichen Code in add_val nicht eingeschlossen werden möchte es zweimal).

#include <iostream>

namespace {
  double a;
  void b(double x)
  {
    a -= x;
  }
  void add_val(double x)
  {
    a += x;
    if(x==0.01) b(0);
    if(x==0.02) b(0.6);
    if(x==0.03) b(-0.1);
    if(x==0.04) b(0.4);
  }
}

int main()
{
  a = 0;
  for(int i=0; i<1000000000; ++i)
    {
      add_val(i*1e-10);
    }
  std::cout << a << '\n';
  return 0;
}
Xioxox
quelle
5
Zu schön um wahr zu sein - ich habe dieses Segment auf gcc 4-1-2 mit O3-Optimierung mit und ohne Namespace-Anweisung ausprobiert: -> Habe die gleiche Zeit (3 Sek., Mit -O3 und 4 Sek. Mit -O3)
Theo
2
Dieser Code war absichtlich komplex, um den Compiler davon zu überzeugen, b und add_val nicht in main einzubinden. Bei der O3-Optimierung wird unabhängig von den Kosten für das Aufblähen des Codes viel Inlining verwendet. Es gibt jedoch wahrscheinlich noch Funktionen, bei denen O3 add_val nicht inline würde. Sie können versuchen, add_val komplexer zu gestalten oder es unter verschiedenen Umständen mehrmals von main aus aufzurufen.
Xioxox
5
@ Daniel: Was vermisse ich? Wie gelesen, sagten Sie, Sie hätten mit -O3sich selbst verglichen , dann sagten Sie, 3 gegen 4 Sekunden seien "die gleiche Zeit". beides macht keinen Sinn. Ich vermute, die wirkliche Erklärung würde, aber was ist es?
underscore_d
@underscore_d Die Antwort lautet -O2 wurde in beiden Fällen verwendet, nicht -O3. Unterschiedliche Optimierungsstufen können sich unterschiedlich verhalten. Außerdem können sich verschiedene Compilerversionen unterschiedlich verhalten (die Antwort kann also veraltet sein)
Paul Stelian
1
@PaulStelian Ich weiß das, aber es scheint ziemlich klar zu sein, dass ich nicht auf die Antwort von xioxox, sondern auf Theos Kommentar geantwortet habe (obwohl sich entweder sein Name geändert hat oder ich irgendwie durcheinander geraten bin)
underscore_d
12

Das Beispiel zeigt, dass die Personen in dem Projekt, dem Sie beigetreten sind, anonyme Namespaces nicht verstehen :)

namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

Diese müssen sich nicht in einem anonymen Namespace befinden, da das constObjekt bereits eine statische Verknüpfung aufweist und daher möglicherweise nicht mit gleichnamigen Bezeichnern in einer anderen Übersetzungseinheit in Konflikt stehen kann.

    bool getState(userType*,otherUserType*);
}

Und das ist eigentlich eine Pessimisierung: getState()hat externe Verknüpfung. Es ist normalerweise besser, eine statische Verknüpfung zu bevorzugen, da dies die Symboltabelle nicht verschmutzt. Es ist besser zu schreiben

static bool getState(/*...*/);

Hier. Ich bin in dieselbe Falle geraten (es gibt Formulierungen im Standard, die darauf hindeuten, dass die Dateistatik zugunsten anonymer Namespaces irgendwie veraltet ist), aber bei der Arbeit in einem großen C ++ - Projekt wie KDE gibt es viele Leute, die Ihren Kopf in die richtige Richtung drehen wieder da :)

Marc Mutz - mmutz
quelle
10
Da c ++ 11 unbenannte Namespaces eine interne Verknüpfung haben (Abschnitt 3.5 im Standard oder en.cppreference.com/w/cpp/language/namespace#Unnamed_namespaces )
Emile Vrijdags
11
"Diese müssen sich nicht in einem anonymen Namespace befinden" Technisch gesehen sicher - aber es tut trotzdem nicht weh, sie als visuelle Erinnerung an ihre Semantik in einen zu setzen und es (noch mehr) trivial zu machen, constNess zu entfernen später auf Wunsch. Ich bezweifle, dass das OP-Team nichts "versteht"! Außerdem ist das Bit über Funktionen in anonymen Namespaces mit externer Verknüpfung ab C ++ 11 falsch, wie bereits erwähnt. Nach meinem Verständnis haben sie ein Problem mit Vorlagenargumenten behoben, für die zuvor eine externe Verknüpfung erforderlich war, sodass unbenannte Namespaces (die Vorlagenargumente enthalten können) eine interne Verknüpfung aufweisen können.
underscore_d
11

Ein anonymer Namespace stellt die eingeschlossenen Variablen, Funktionen, Klassen usw. nur in dieser Datei zur Verfügung. In Ihrem Beispiel können Sie globale Variablen vermeiden. Es gibt keinen Leistungsunterschied zur Laufzeit oder zur Kompilierungszeit.

Abgesehen von "Soll diese Variable, Funktion, Klasse usw. öffentlich oder privat sein?" Gibt es nicht so viele Vor- oder Nachteile.

Max Lybbert
quelle
2
Es kann Leistungsunterschiede geben - siehe meine Antwort hier. Dadurch kann der Compiler den Code besser optimieren.
Xioxox
2
Du hast einen Punkt; Zumindest so weit wie C ++ heute ist. Für C ++ 98 / C ++ 03 sind jedoch externe Verknüpfungen erforderlich, um als Vorlagenargumente verwendet zu werden. Da Dinge in anonymen Namespaces als Vorlagenargumente verfügbar sind, würden sie eine externe Verknüpfung haben (zumindest in Pre-C ++ 11), selbst wenn es keine Möglichkeit gäbe, von außerhalb der Datei auf sie zu verweisen. Ich denke, es gab möglicherweise eine gewisse Möglichkeit, dies zu verfälschen, da der Standard nur verlangt, dass die Dinge so tun, als ob die Regeln durchgesetzt würden. und manchmal ist es möglich, dies zu tun, ohne die Regeln wirklich durchzusetzen.
Max Lybbert