Jakie są zasady dotyczące używania podkreślenia w identyfikatorze C ++?

Oct 23 2008

W C ++ powszechne jest nazywanie zmiennych składowych za pomocą pewnego rodzaju przedrostka oznaczającego fakt, że są one zmiennymi składowymi, a nie zmiennymi lokalnymi lub parametrami. Jeśli pochodzisz z tła MFC, prawdopodobnie użyjesz m_foo. Widziałem też myFoosporadycznie.

Wydaje się, że C # (lub po prostu .NET) zaleca używanie tylko podkreślenia, jak w _foo. Czy jest to dozwolone przez standard C ++?

Odpowiedzi

868 24revs,13users35%RogerPate Oct 23 2008 at 14:08

Zasady (które nie uległy zmianie w C ++ 11):

  • Zarezerwowane w dowolnym zakresie, w tym do użytku jako makra implementacyjne :
    • identyfikatory zaczynające się od podkreślenia, po którym bezpośrednio następuje duża litera
    • identyfikatory zawierające sąsiadujące podkreślenia (lub „podwójne podkreślenie”)
  • Zarezerwowane w globalnej przestrzeni nazw:
    • identyfikatory zaczynające się od podkreślenia
  • Ponadto wszystko w stdprzestrzeni nazw jest zarezerwowane. (Możesz jednak dodawać specjalizacje szablonów).

Ze standardu C ++ 2003:

17.4.3.1.2 Nazwy globalne [lib.global.names]

Pewne zestawy nazw i sygnatur funkcji są zawsze zastrzeżone dla implementacji:

  • Każda nazwa, która zawiera podwójne podkreślenie ( __) lub zaczyna się od podkreślenia, po którym następuje duża litera (2.11), jest zastrzeżona dla implementacji do dowolnego użytku.
  • Każda nazwa, która zaczyna się od podkreślenia, jest zarezerwowana dla implementacji i może być używana jako nazwa w globalnej przestrzeni nazw. 165

165) Nazwy takie są również zarezerwowane w przestrzeni nazw ::std(17.4.3.1).

Ponieważ C ++ jest oparty na standardzie C (1.1 / 2, C ++ 03), a C99 jest odniesieniem normatywnym (1.2 / 1, C ++ 03), mają one również zastosowanie, ze standardu C z 1999:

7.1.3 Zarezerwowane identyfikatory

Każdy nagłówek deklaruje lub definiuje wszystkie identyfikatory wymienione w skojarzonej z nim podrozdziale i opcjonalnie deklaruje lub definiuje identyfikatory wymienione w powiązanych z nim przyszłych podrozdziałach kierunków bibliotecznych oraz identyfikatory, które są zawsze zarezerwowane do dowolnego użytku lub do użycia jako identyfikatory zakresu zbioru.

  • Wszystkie identyfikatory zaczynające się od podkreślenia i dużej litery lub innego podkreślenia są zawsze zarezerwowane do dowolnego użytku.
  • Wszystkie identyfikatory zaczynające się od podkreślenia są zawsze zarezerwowane do użytku jako identyfikatory z zakresem pliku zarówno w przestrzeni nazw zwykłych, jak i nazw znaczników.
  • Każda nazwa makra w którymkolwiek z poniższych podrozdziałów (łącznie z przyszłymi wskazówkami dotyczącymi biblioteki) jest zarezerwowana do użytku w sposób określony, jeśli uwzględniono którykolwiek z powiązanych z nią nagłówków; chyba że wyraźnie określono inaczej (patrz 7.1.4).
  • Wszystkie identyfikatory z powiązaniami zewnętrznymi w którymkolwiek z poniższych podrozdziałów (łącznie z przyszłymi wskazówkami dotyczącymi biblioteki) są zawsze zarezerwowane do użytku jako identyfikatory z powiązaniami zewnętrznymi. 154
  • Każdy identyfikator z zakresem pliku wymienionym w którymkolwiek z poniższych podrozdziałów (w tym przyszłe wskazówki dotyczące biblioteki) jest zarezerwowany do użytku jako nazwa makra i jako identyfikator z zakresem pliku w tej samej przestrzeni nazw, jeśli uwzględniono którykolwiek z powiązanych z nim nagłówków.

Żadne inne identyfikatory nie są zarezerwowane. Jeśli program deklaruje lub definiuje identyfikator w kontekście, w którym jest on zarezerwowany (inny niż dozwolony w 7.1.4), lub definiuje zarezerwowany identyfikator jako nazwę makra, zachowanie jest niezdefiniowane.

Jeśli program usunie (wraz z #undef) jakąkolwiek definicją makra identyfikatora z pierwszej grupy wymienionej powyżej, zachowanie jest niezdefiniowane.

154) Lista zarezerwowanych identyfikatorów z zewnętrznym wiązaniem obejmuje errno, math_errhandling, setjmp, i va_end.

Mogą obowiązywać inne ograniczenia. Na przykład standard POSIX rezerwuje wiele identyfikatorów, które prawdopodobnie pojawią się w normalnym kodzie:

  • Nazwy zaczynające się od dużej Elitery, po której następuje cyfra lub wielka litera:
    • mogą być używane dla dodatkowych nazw kodów błędów.
  • Nazwy zaczynające się od małej litery islub tozakończone małą literą
    • może być używany do dodatkowego testowania znaków i funkcji konwersji.
  • Nazwy zaczynające się od, LC_po których następuje duża litera
    • może być używany do dodatkowych makr określających atrybuty ustawień regionalnych.
  • Nazwy wszystkich istniejących funkcji matematycznych z przyrostkiem flub lsą zastrzeżone
    • dla odpowiednich funkcji, które działają odpowiednio na argumentach zmiennoprzecinkowych i długich podwójnych argumentach.
  • Nazwy zaczynające się od, SIGpo których następuje duża litera, są zastrzeżone
    • dodatkowe nazwy sygnałów.
  • Nazwy zaczynające się od, SIG_po których następuje duża litera, są zastrzeżone
    • dla dodatkowych działań sygnalizacyjnych.
  • Nazwiska zaczynające się str, memczy wcsnastępuje listem małymi zastrzeżone
    • dla dodatkowych funkcji ciągów i tablic.
  • Nazwiska zaczynające się PRIlub SCNnastępuje każdej litery małymi lub Xsą zastrzeżone
    • dla dodatkowych makr specyfikatorów formatu
  • Nazwy kończące się na _tsą zastrzeżone
    • dodatkowe nazwy typów.

Chociaż używanie tych nazw do własnych celów w tej chwili może nie powodować problemu, to jednak stwarzają one możliwość konfliktu z przyszłymi wersjami tego standardu.


Osobiście po prostu nie zaczynam identyfikatorów od podkreślenia. Nowy dodatek do mojej reguły: nie używaj nigdzie podwójnych podkreśleń, co jest łatwe, ponieważ rzadko używam podkreślenia.

Po zbadaniu tego artykułu nie kończę już moich identyfikatorów, _tponieważ jest to zastrzeżone przez standard POSIX.

Zasada dotycząca dowolnego identyfikatora kończącego się na _tbardzo mnie zaskoczyła. Myślę, że to standard POSIX (jeszcze nie jestem pewien) szukający wyjaśnień oraz oficjalnego rozdziału i wersetu. To pochodzi z podręcznika GNU libtool , zawierającego nazwy zastrzeżone.

CesarB podał poniższy link do zastrzeżonych symboli POSIX 2004 i zauważa, że ​​„można tam znaleźć wiele innych zastrzeżonych przedrostków i przyrostków ...”. W POSIX 2008 zastrzeżone symbole zdefiniowane są tutaj. Ograniczenia są nieco bardziej zniuansowane niż powyższe.

202 paercebal Oct 23 2008 at 14:27

Zasady unikania kolizji nazw są zarówno w standardzie C ++ (patrz książka Stroustrupa), jak i wspomniane przez guru C ++ (Sutter itp.).

Zasada osobista

Ponieważ nie chciałem zajmować się przypadkami i chciałem prostej zasady, zaprojektowałem osobistą, która jest zarówno prosta, jak i poprawna:

Nazywając symbol, unikniesz kolizji z bibliotekami kompilatora / systemu operacyjnego / standardu, jeśli:

  • nigdy nie zaczynaj symbolu od podkreślenia
  • nigdy nie nazywaj symbolu z dwoma kolejnymi podkreśleniami w środku.

Oczywiście umieszczenie kodu w unikalnej przestrzeni nazw pomaga również uniknąć kolizji (ale nie chroni przed złymi makrami)

Kilka przykładów

(Używam makr, ponieważ są one bardziej zanieczyszczające kod symboli C / C ++, ale może to być wszystko, od nazwy zmiennej do nazwy klasy)

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

Wyciągi z wersji roboczej C ++ 0x

Z pliku n3242.pdf (oczekuję, że ostateczny standardowy tekst będzie podobny):

17.6.3.3.2 Nazwy globalne [global.names]

Pewne zestawy nazw i sygnatur funkcji są zawsze zastrzeżone dla implementacji:

- Każda nazwa, która zawiera podwójne podkreślenie _ _ lub zaczyna się od podkreślenia, po którym następuje duża litera (2.12), jest zastrzeżona dla implementacji do dowolnego użytku.

- Każda nazwa, która zaczyna się od podkreślenia, jest zarezerwowana dla implementacji do użycia jako nazwa w globalnej przestrzeni nazw.

Ale również:

17.6.3.3.5 Zdefiniowane przez użytkownika sufiksy literałów [usrlit.suffix]

Literalne identyfikatory sufiksów, które nie zaczynają się od podkreślenia, są zarezerwowane dla przyszłej standaryzacji.

Ta ostatnia klauzula jest myląca, chyba że uznasz, że nazwa zaczynająca się od jednego podkreślenia i zakończona małą literą byłaby OK, gdyby nie została zdefiniowana w globalnej przestrzeni nazw ...

40 RogerLipscombe Oct 23 2008 at 14:06

Z MSDN :

Użycie dwóch kolejnych znaków podkreślenia (__) na początku identyfikatora lub pojedynczego początkowego podkreślenia, po którym następuje duża litera, jest zarezerwowane dla implementacji C ++ we wszystkich zakresach. Należy unikać używania jednego wiodącego podkreślenia, po którym następuje mała litera w nazwach z zakresem pliku, ze względu na możliwe konflikty z obecnymi lub przyszłymi zastrzeżonymi identyfikatorami.

Oznacza to, że możesz użyć pojedynczego podkreślenia jako przedrostka zmiennej składowej, o ile występuje po nim mała litera.

Najwyraźniej pochodzi to z sekcji 17.4.3.1.2 standardu C ++, ale nie mogę znaleźć oryginalnego źródła pełnego standardu w Internecie.

Zobacz także to pytanie .

25 MaxLybbert Nov 15 2008 at 03:03

Jeśli chodzi o drugą część pytania, często umieszcza się podkreślenie na końcu nazwy zmiennej, aby nie kolidować z niczym wewnętrznym.

Robię to nawet w klasach i przestrzeniach nazw, ponieważ wtedy muszę zapamiętać tylko jedną regułę (w porównaniu do „na końcu nazwy w zasięgu globalnym i początku nazwy wszędzie indziej”).

1 JohnMillikin Oct 23 2008 at 14:05

Tak, podkreślenia mogą być używane w dowolnym miejscu identyfikatora. Uważam, że zasady są następujące: dowolne z az, AZ, _ w pierwszym znaku i te + 0-9 dla kolejnych znaków.

Przedrostki podkreślenia są powszechne w kodzie C - pojedynczy znak podkreślenia oznacza „prywatny”, a podwójne podkreślenie jest zwykle zarezerwowane do użytku przez kompilator.