Welche Regeln gelten für die Verwendung eines Unterstrichs in einem C ++ - Bezeichner?

930

In C ++ ist es üblich, Mitgliedsvariablen mit einem Präfix zu benennen, um die Tatsache anzuzeigen, dass es sich um Mitgliedsvariablen handelt und nicht um lokale Variablen oder Parameter. Wenn Sie aus einem MFC-Hintergrund stammen, werden Sie wahrscheinlich verwenden m_foo. Ich habe auch myFoogelegentlich gesehen .

C # (oder möglicherweise nur .NET) scheint zu empfehlen, nur einen Unterstrich zu verwenden, wie in _foo. Ist dies nach dem C ++ - Standard zulässig?

Roger Lipscombe
quelle
6
Nur um zu beachten, dass die Unkenntnis dieser Regeln nicht unbedingt bedeutet, dass Ihr Code nicht kompiliert oder ausgeführt wird, aber es ist wahrscheinlich, dass Ihr Code nicht auf verschiedene Compiler und Versionen portierbar ist, da nicht garantiert werden kann, dass es keinen Namen gibt Zusammenstöße. Um dies zu belegen, kenne ich eine bestimmte Implementierung eines wichtigen Systems, das überall als Namenskonvention den Großbuchstaben verwendet hat. Dort gab es keine Fehler aufgrund dessen. Natürlich ist es eine schlechte Praxis.
G24l

Antworten:

852

Die Regeln (die sich in C ++ 11 nicht geändert haben):

  • In jedem Bereich reserviert, auch zur Verwendung als Implementierungsmakros :
    • Bezeichner, die mit einem Unterstrich beginnen, unmittelbar gefolgt von einem Großbuchstaben
    • Bezeichner mit benachbarten Unterstrichen (oder "doppeltem Unterstrich")
  • Reserviert im globalen Namespace:
    • Bezeichner, die mit einem Unterstrich beginnen
  • Außerdem ist alles im stdNamespace reserviert. (Sie können jedoch Vorlagenspezialisierungen hinzufügen.)

Aus dem C ++ Standard 2003:

17.4.3.1.2 Globale Namen [lib.global.names]

Bestimmte Sätze von Namen und Funktionssignaturen sind immer der Implementierung vorbehalten:

  • Jeder Name, der einen doppelten Unterstrich ( __) enthält oder mit einem Unterstrich gefolgt von einem Großbuchstaben (2.11) beginnt, ist der Implementierung für jede Verwendung vorbehalten.
  • Jeder Name, der mit einem Unterstrich beginnt, ist der Implementierung zur Verwendung als Name im globalen Namespace vorbehalten. 165

165) Solche Namen sind auch im Namespace ::std(17.4.3.1) reserviert .

Da C ++ auf dem C-Standard (1.1 / 2, C ++ 03) basiert und C99 eine normative Referenz (1.2 / 1, C ++ 03) ist, gelten diese auch ab dem C-Standard von 1999:

7.1.3 Reservierte Kennungen

Jeder Header deklariert oder definiert alle in seinem zugeordneten Unterabschnitt aufgelisteten Bezeichner und deklariert oder definiert optional Bezeichner, die in seinem zugeordneten zukünftigen Bibliotheksanweisungsunterabschnitt und Bezeichnern aufgeführt sind, die immer entweder für eine Verwendung oder zur Verwendung als Dateibereichsbezeichner reserviert sind.

  • Alle Bezeichner, die mit einem Unterstrich und entweder einem Großbuchstaben oder einem anderen Unterstrich beginnen, sind immer für jede Verwendung reserviert.
  • Alle Bezeichner, die mit einem Unterstrich beginnen, sind immer für die Verwendung als Bezeichner mit Dateibereich sowohl im normalen als auch im Tag-Namensraum reserviert.
  • Jeder Makroname in einem der folgenden Unterabschnitte (einschließlich der zukünftigen Bibliotheksanweisungen) ist für die Verwendung wie angegeben reserviert, wenn einer der zugehörigen Header enthalten ist. sofern nicht ausdrücklich anders angegeben (siehe 7.1.4).
  • Alle Bezeichner mit externer Verknüpfung in einem der folgenden Unterabschnitte (einschließlich der zukünftigen Bibliotheksanweisungen) sind immer für die Verwendung als Bezeichner mit externer Verknüpfung reserviert. 154
  • Jeder Bezeichner mit Dateibereich, der in einem der folgenden Unterabschnitte aufgeführt ist (einschließlich der zukünftigen Bibliotheksanweisungen), ist für die Verwendung als Makroname und als Bezeichner mit Dateibereich im selben Namensraum reserviert, wenn einer der zugehörigen Header enthalten ist.

Es sind keine anderen Bezeichner reserviert. Wenn das Programm einen Bezeichner in einem Kontext deklariert oder definiert, in dem es reserviert ist (anders als in 7.1.4 zulässig), oder einen reservierten Bezeichner als Makronamen definiert, ist das Verhalten undefiniert.

Wenn das Programm #undefeine Makrodefinition eines Bezeichners in der ersten oben aufgeführten Gruppe entfernt (mit ), ist das Verhalten undefiniert.

154) Die Liste der reservierten Bezeichner mit externer Bindung enthält errno, math_errhandling, setjmp, und va_end.

Möglicherweise gelten andere Einschränkungen. Zum Beispiel reserviert der POSIX-Standard viele Bezeichner, die wahrscheinlich im normalen Code angezeigt werden:

  • Namen, die mit einem Großbuchstaben beginnen, Efolgten einer Ziffer oder einem Großbuchstaben:
    • kann für zusätzliche Fehlercode-Namen verwendet werden.
  • Namen, die entweder mit isoder togefolgt von einem Kleinbuchstaben beginnen
    • kann für zusätzliche Zeichentest- und Konvertierungsfunktionen verwendet werden.
  • Namen, die mit LC_einem Großbuchstaben beginnen
    • kann für zusätzliche Makros verwendet werden, die Gebietsschemaattribute angeben.
  • Namen aller vorhandenen mathematischen Funktionen, denen ein Suffix hinzugefügt wurde foder ldie reserviert sind
    • für entsprechende Funktionen, die mit float- bzw. langen Doppelargumenten arbeiten.
  • Namen, die mit SIGeinem Großbuchstaben beginnen, sind reserviert
    • für zusätzliche Signalnamen.
  • Namen, die mit SIG_einem Großbuchstaben beginnen, sind reserviert
    • für zusätzliche Signalaktionen.
  • Namen, die mit str, memoder wcsgefolgt von einem Kleinbuchstaben beginnen, sind reserviert
    • für zusätzliche String- und Array-Funktionen.
  • Namen, die mit PRIoder SCNgefolgt von Kleinbuchstaben beginnen oder Xreserviert sind
    • für zusätzliche Formatbezeichner-Makros
  • Namen, die mit enden, _tsind reserviert
    • für zusätzliche Typnamen.

Obwohl die Verwendung dieser Namen für Ihre eigenen Zwecke im Moment möglicherweise kein Problem darstellt, besteht die Möglichkeit eines Konflikts mit zukünftigen Versionen dieses Standards.


Persönlich beginne ich Bezeichner einfach nicht mit Unterstrichen. Neu in meiner Regel: Verwenden Sie nirgendwo doppelte Unterstriche, was einfach ist, da ich selten Unterstriche verwende.

Nachdem ich diesen Artikel recherchiert habe, beende ich meine Bezeichner nicht mehr mit, _t da dies dem POSIX-Standard vorbehalten ist.

Die Regel, dass ein Bezeichner mit endet _t, hat mich sehr überrascht. Ich denke, das ist ein POSIX-Standard (noch nicht sicher), der nach Klarstellung und offiziellem Kapitel und Vers sucht. Dies ist aus dem GNU libtool-Handbuch , in dem reservierte Namen aufgeführt sind.

CesarB stellte den folgenden Link zu den reservierten POSIX 2004- Symbolen und Notizen bereit , "dass dort viele andere reservierte Präfixe und Suffixe ... zu finden sind". Hier werden die reservierten POSIX 2008- Symbole definiert. Die Einschränkungen sind etwas differenzierter als die oben genannten.

Roger Pate
quelle
14
Der C ++ - Standard "importiert" den C-Standard nicht, oder? Sie importieren bestimmte Header, aber nicht die Sprache als Ganzes oder Namensregeln, soweit ich weiß. Aber ja, der hat mich auch überrascht. Da es sich jedoch um C handelt, kann es nur für die globalen ns gelten. Sollte sicher sein, _t innerhalb von Klassen zu verwenden, während ich es lese
jalf
27
Der C ++ Standard "importiert" den C Standard nicht. Es verweist auf den C-Standard. In der Einführung zur C ++ - Bibliothek heißt es: "Die Bibliothek stellt auch die Einrichtungen der Standard C-Bibliothek zur Verfügung." Dies geschieht durch Einfügen von Headern der C-Standard-Bibliothek mit entsprechenden Änderungen, jedoch nicht durch "Importieren". Der C ++ - Standard verfügt über eigene Regeln, die die reservierten Namen beschreiben. Wenn ein in C reservierter Name in C ++ reserviert werden soll, ist dies der richtige Ort, um dies zu sagen. Der C ++ Standard sagt dies jedoch nicht. Ich glaube also nicht, dass in C reservierte Dinge in C ++ reserviert sind - aber ich könnte mich irren.
Johannes Schaub - litb
8
Folgendes habe ich zum Problem "_t" herausgefunden: n1256 (C99 TC3) sagt: "Typedef-Namen, die mit int oder uint beginnen und mit _t enden", sind reserviert. Ich denke, das erlaubt immer noch die Verwendung von Namen wie "foo_t" - aber ich denke, diese werden dann von POSIX reserviert.
Johannes Schaub - litb
59
Also ist 'Toleranz' von POSIX reserviert, da es mit 'bis' + einem Kleinbuchstaben beginnt? Ich wette, viel Code verstößt gegen diese Regel!
Sjoerd
23
@LokiAstari, " Der C ++ - Standard wird in Bezug auf den C-Standard definiert. Grundsätzlich heißt es, dass C ++ C mit diesen Unterschieden und Ergänzungen ist. " Unsinn! C ++ verweist nur auf den C-Standard in [basic.fundamental] und in der Bibliothek. Wenn das, was Sie sagen, wahr ist, wo sagt C ++ das _Boolund _Imaginaryexistiert in C ++ nicht? Die C ++ - Sprache wird explizit definiert, nicht in Form von "Änderungen" an C, da sonst der Standard viel kürzer sein könnte!
Jonathan Wakely
198

Die Regeln zur Vermeidung von Namenskollisionen sind sowohl im C ++ - Standard (siehe Stroustrup-Buch) als auch von C ++ - Gurus (Sutter usw.) erwähnt.

Persönliche Regel

Da ich mich nicht mit Fällen befassen wollte und eine einfache Regel wollte, habe ich eine persönliche Regel entworfen , die sowohl einfach als auch korrekt ist:

Wenn Sie ein Symbol benennen, vermeiden Sie Kollisionen mit Compiler- / Betriebssystem- / Standardbibliotheken, wenn Sie:

  • Beginnen Sie ein Symbol niemals mit einem Unterstrich
  • Nennen Sie niemals ein Symbol mit zwei aufeinander folgenden Unterstrichen.

Wenn Sie Ihren Code in einen eindeutigen Namespace einfügen, können Sie natürlich auch Kollisionen vermeiden (schützt jedoch nicht vor bösen Makros).

Einige Beispiele

(Ich verwende Makros, weil sie die C / C ++ - Symbole stärker durch Code verschmutzen, aber es kann alles sein, vom Variablennamen bis zum Klassennamen.)

#define _WRONG
#define __WRONG_AGAIN
#define RIGHT_
#define WRONG__WRONG
#define RIGHT_RIGHT
#define RIGHT_x_RIGHT

Auszüge aus dem C ++ 0x-Entwurf

Aus der Datei n3242.pdf (ich erwarte, dass der endgültige Standardtext ähnlich ist):

17.6.3.3.2 Globale Namen [global.names]

Bestimmte Sätze von Namen und Funktionssignaturen sind immer der Implementierung vorbehalten:

- Jeder Name, der einen doppelten Unterstrich _ _ enthält oder mit einem Unterstrich gefolgt von einem Großbuchstaben (2.12) beginnt, ist der Implementierung für jede Verwendung vorbehalten.

- Jeder Name, der mit einem Unterstrich beginnt, ist der Implementierung zur Verwendung als Name im globalen Namespace vorbehalten.

Aber auch:

17.6.3.3.5 Benutzerdefinierte Literal-Suffixe [usrlit.suffix]

Literale Suffix-IDs, die nicht mit einem Unterstrich beginnen, sind für die zukünftige Standardisierung reserviert.

Diese letzte Klausel ist verwirrend, es sei denn, Sie denken, dass ein Name, der mit einem Unterstrich beginnt und auf den ein Kleinbuchstabe folgt, in Ordnung wäre, wenn er nicht im globalen Namespace definiert wäre ...

paercebal
quelle
9
@Meysam: __WRONG_AGAIN__Enthält zwei aufeinanderfolgende Unterstriche (zwei am Anfang und zwei am Ende), daher ist dies gemäß dem Standard falsch.
Paercebal
8
@ BЈовић: WRONG__WRONGenthält zwei aufeinanderfolgende Unterstriche (zwei in der Mitte), so dass dies gemäß dem Standard falsch ist
paercebal
2
Wenn Sie Ihren Code in einen eindeutigen Namespace einfügen, können Sie auch Kollisionen vermeiden. Dies reicht jedoch immer noch nicht aus, da der Bezeichner unabhängig vom Gültigkeitsbereich (z __attribute__. B. für GCC) möglicherweise mit einem Schlüsselwort kollidiert .
Ruslan
1
Warum gibt es ein Problem, wenn zwei aufeinanderfolgende Unterstriche gemäß dem Standard in der Mitte stehen ? Benutzerdefinierte Literal-Suffixe gelten für Literalwerte wie 1234567Loder 4.0f; IIRC bezieht sich dies auf ohttp: //en.cppreference.com/w/cpp/language/user_literal
Jason S
2
Why is there any problem of having two consecutive underscores in the middle according to the standard?Weil der Standard sagt, dass diese reserviert sind. Dies ist kein Ratschlag für guten oder schlechten Stil. Es ist eine Entscheidung aus dem Standard. Warum haben sie das entschieden? Ich denke, die ersten Compiler haben solche Konventionen bereits vor der Standardisierung informell verwendet.
Paercebal
38

Von MSDN :

Die Verwendung von zwei aufeinander folgenden Unterstrichen (__) am Anfang eines Bezeichners oder eines einzelnen führenden Unterstrichs gefolgt von einem Großbuchstaben ist für C ++ - Implementierungen in allen Bereichen reserviert. Sie sollten vermeiden, einen führenden Unterstrich gefolgt von einem Kleinbuchstaben für Namen mit Dateibereich zu verwenden, da möglicherweise Konflikte mit aktuellen oder zukünftigen reservierten Bezeichnern auftreten.

Dies bedeutet, dass Sie einen einzelnen Unterstrich als Präfix für eine Mitgliedsvariable verwenden können, sofern ein Kleinbuchstabe folgt.

Dies stammt anscheinend aus Abschnitt 17.4.3.1.2 des C ++ - Standards, aber ich kann online keine Originalquelle für den vollständigen Standard finden.

Siehe auch diese Frage .

Roger Lipscombe
quelle
2
Ich fand einen ähnlichen Text in n3092.pdf (dem Entwurf des C ++ 0x-Standards) im Abschnitt: "17.6.3.3.2 Globale Namen"
paercebal
7
Interessanterweise scheint dies die einzige Antwort zu sein, die eine direkte und präzise Antwort auf die Frage bietet.
Hyde
9
@hyde: Eigentlich nicht, da die Regel übersprungen wird, keine Bezeichner mit einem führenden Unterstrich im globalen Namespace zu haben. Siehe Rogers Antwort . Ich würde mich sehr vor Zitaten von MS VC-Dokumenten als Autorität für den C ++ - Standard hüten.
sbi
@sbi Ich bezog mich in dieser Antwort auf "Sie können einen einzelnen Unterstrich als Präfix für eine Mitgliedsvariable verwenden, solange ein Kleinbuchstabe folgt" , die die Frage im Fragentext direkt und präzise beantwortet, ohne zu ertrinken in einer Textwand.
Hyde
5
Erstens halte ich das Fehlen eines Hinweises, dass dieselbe Regel nicht für den globalen Namespace gilt, immer noch für einen Fehler. Schlimmer ist jedoch, dass benachbarte Unterstriche nicht nur zu Beginn, sondern überall in einer Kennung verboten sind . Diese Antwort lässt also nicht nur eine Tatsache aus, sondern macht tatsächlich mindestens eine aktiv falsche Behauptung. Wie gesagt, ich würde nicht auf die MSVC-Dokumente verweisen, es sei denn, die Frage bezieht sich ausschließlich auf VC.
sbi
25

Was den anderen Teil der Frage betrifft, ist es üblich, den Unterstrich am Ende des Variablennamens zu setzen, um nicht mit internen Elementen in Konflikt zu geraten.

Ich mache das sogar innerhalb von Klassen und Namespaces, weil ich mich dann nur noch an eine Regel erinnern muss (im Vergleich zu "am Ende des Namens im globalen Bereich und am Anfang des Namens überall sonst").

Max Lybbert
quelle
2

Ja, Unterstriche können überall in einem Bezeichner verwendet werden. Ich glaube, die Regeln sind: az, AZ, _ im ersten Zeichen und diese + 0-9 für die folgenden Zeichen.

Unterstrichpräfixe sind im C-Code üblich - ein einzelner Unterstrich bedeutet "privat", und doppelte Unterstriche sind normalerweise für die Verwendung durch den Compiler reserviert.

John Millikin
quelle
3
Sie sind in Bibliotheken üblich. Sie sollten im Benutzercode nicht häufig vorkommen.
Martin York
43
Menschen tun Schreib Bibliotheken in C, die Sie kennen.
John Millikin
7
"Ja, Unterstriche können überall in einer Kennung verwendet werden." Dies ist für globale Bezeichner falsch. Siehe Rogers Antwort .
sbi