Які правила використання підкреслення в ідентифікаторі C ++?


930

У C ++ прийнято називати член-змінними з певним префіксом для позначення факту, що вони є змінними-членами, а не локальними змінними чи параметрами. Якщо ви прийшли з фону MFC, ви, ймовірно, будете використовувати m_foo. Я також myFooчасто бував.

Здається, C # (або, можливо, просто .NET) рекомендує використовувати лише підкреслення, як у _foo. Це дозволено стандартом C ++?


3
Сторінку посібника glibc про це можна знайти на веб- сайті gnu.org/software/libc/manual/html_node/Reserved-Names.html Редагувати: див. Також opengroup.org/onlinepubs/009695399/functions/xsh_chap02_02.html
CesarB

6
Зауважимо лише, що незнання цих правил не означає, що ваш код не буде компілюватися чи запускатися, але, ймовірно, ваш код не буде переносним для різних компіляторів та версії, оскільки не можна гарантувати, що ім’я не буде сутички. Щоб підтвердити це, я знаю про певну реалізацію важливої ​​системи, яка використовує як угоду про іменування велику літеру _ скрізь. Там, де помилок через це немає. Звичайно, це погана практика.
g24l

Відповіді:


852

Правила (які не змінювалися в C ++ 11):

  • Зарезервовано в будь-якому обсязі, включаючи для використання в якості макросів реалізації :
    • ідентифікатори, що починаються з підкреслення, одразу після великої літери
    • ідентифікатори, що містять суміжні підкреслення (або "подвійне підкреслення")
  • Зарезервовано у глобальному просторі імен:
    • ідентифікатори, що починаються з підкреслення
  • Також все в stdпросторі імен зарезервовано. (Однак вам дозволяється додавати спеціалізації для шаблонів.)

Зі стандарту C ++ 2003 року:

17.4.3.1.2 Глобальні імена [lib.global.names]

Певні набори імен та підписів функцій завжди зарезервовані для реалізації:

  • Кожне ім'я, яке містить подвійне підкреслення ( __) або починається з підкреслення, за яким прописна літера великого розміру (2.11), зарезервоване для реалізації для будь-якого використання.
  • Кожне ім'я, яке починається з підкреслення, зарезервоване для реалізації для використання в якості імені в глобальному просторі імен. 165

165) Такі імена також зарезервовані у просторі імен ::std(17.4.3.1).

Оскільки C ++ базується на стандарті C (1.1 / 2, C ++ 03) і C99 є нормативним посиланням (1.2 / 1, C ++ 03), вони також застосовуються, починаючи зі стандарту C 1999:

7.1.3 Зарезервовані ідентифікатори

Кожен заголовок декларує або визначає всі ідентифікатори, перелічені у відповідному підпункті, та необов'язково декларує або визначає ідентифікатори, перелічені у відповідному підрозділі майбутніх напрямків бібліотеки та ідентифікатори, які завжди зарезервовані або для будь-якого використання, або для використання як ідентифікаторів обсягу файлів.

  • Усі ідентифікатори, що починаються з підкреслення, або великої літери, або іншого підкреслення завжди зарезервовані для будь-якого використання.
  • Усі ідентифікатори, які починаються з підкреслення, завжди зарезервовані для використання в якості ідентифікаторів з областю файлу як у звичайному просторі, так і в просторах імен тегів.
  • Кожне ім'я макросу в будь-якому з наступних підрозділів (включаючи майбутні вказівки бібліотеки) зарезервоване для використання, як зазначено, якщо включений будь-який з його пов'язаних заголовків; якщо прямо не вказано інше (див. 7.1.4).
  • Усі ідентифікатори із зовнішнім зв’язком у будь-якому з наступних підрозділів (включаючи майбутні вказівки бібліотеки) завжди зарезервовані для використання в якості ідентифікаторів із зовнішнім зв’язком. 154
  • Кожен ідентифікатор із областю файлів, переліченим у будь-якому з наступних підрозділів (включаючи майбутні вказівки до бібліотеки), зарезервований для використання як ім’я макросу та як ідентифікатор з областю файлу в тому ж просторі імен, якщо включений будь-який з його пов'язаних заголовків.

Інші ідентифікатори не зарезервовані. Якщо програма оголошує або визначає ідентифікатор у контексті, в якому він зарезервований (крім випадків, передбачених 7.1.4), або визначає зарезервований ідентифікатор як ім’я макросу, поведінка не визначається.

Якщо програма видаляє (з #undef) будь-яке макрозначення ідентифікатора першої групи, перерахованої вище, поведінка не визначена.

154) Перелік зарезервованих ідентифікаторів з зовнішньої зв'язком включає в себе errno, math_errhandling, setjmp, і va_end.

Можуть застосовуватися інші обмеження. Наприклад, стандарт POSIX резервує безліч ідентифікаторів, які, ймовірно, відображаються у звичайному коді:

  • Імена, що починаються з великої літери, Eсупроводжуються цифрою чи великою літерою:
    • може використовуватися для додаткових імен помилок.
  • Імена, які починаються з isабо toз малої літери
    • може використовуватися для додаткових функцій тестування та перетворення символів.
  • Імена, які починаються з LC_наступної великої літери
    • може використовуватися для додаткових макросів із зазначенням локальних атрибутів.
  • Назви всіх існуючих математичних функцій із суфіксом fабо lзарезервовані
    • для відповідних функцій, що працюють на плаваючі та довгі подвійні аргументи відповідно.
  • Імена, які починаються з SIGвеликої літери, зарезервовані
    • для додаткових імен сигналу.
  • Імена, які починаються з SIG_великої літери, зарезервовані
    • для додаткових сигнальних дій.
  • Імена , що починаються з str, memабо з wcsподальшим рядкової буквою захищені
    • для додаткових функцій рядків і масивів.
  • Імена, що починаються з PRIабо SCNслідують за будь-якою малою літерою, або Xзарезервовані
    • для додаткових макросів специфікатора формату
  • Імена, які закінчуються _t, зарезервовані
    • для додаткових імен типів

Хоча використання цих імен для власних цілей зараз може не спричинити проблем, вони створюють можливість конфлікту з майбутніми версіями цього стандарту.


Особисто я просто не запускаю ідентифікатори з підкресленнями. Нове доповнення до мого правила: ніколи не використовуйте подвійні підкреслення, що легко, оскільки я рідко використовую підкреслення.

Після дослідження цієї статті я більше не закінчую свої ідентифікатори, _t оскільки це зарезервовано стандартом POSIX.

Правило про будь-який ідентифікатор, що закінчується, _tмене дуже здивувало. Я думаю, що це стандарт POSIX (ще не впевнений), який шукає роз'яснення та офіційний розділ та вірш. Це в посібнику з лібтуалу GNU , в якому перераховані зарезервовані імена.

CesarB надав наступне посилання на зарезервовані символи POSIX 2004 та ", що багато інших зарезервованих префіксів та суфіксів ... можна знайти там". Тут визначені зарезервовані символи POSIX 2008 . Обмеження дещо більш нюансовані, ніж вище.


14
Стандарт C ++ не "імпортує" C, чи не так? Наскільки я знаю, вони імпортують певні заголовки, але не мову в цілому або правила називання. Але так, і той мене здивував. Але оскільки це C, він може застосовуватися лише до глобальних росіян. Має бути безпечним для використання _t всередині класів, коли я його читав
jalf

27
Стандарт C ++ не "імпортує" стандарт C. Він посилається на стандарт C. У вступі бібліотеки C ++ йдеться про те, що "Бібліотека також робить доступними засоби Стандартної бібліотеки С". Це робиться шляхом включення заголовків бібліотеки C Standard із відповідними змінами, але не шляхом "імпорту". Стандарт C ++ має власний набір правил, який описує зарезервовані імена. Якщо ім'я, зарезервоване в C, має бути зарезервоване в C ++, це місце, щоб сказати це. Але стандарт C ++ не говорить про це. Тому я не вірю, що речі, зарезервовані у C, зарезервовані у C ++ - але я цілком можу помилитися.
Йоханнес Шауб - ліб

8
Це те, що я знайшов у питанні "_t": n1256 (C99 TC3) говорить: "Імена typedef, що починаються з int або uint і закінчуються _t", зарезервовані. Я думаю, що все ще дозволяє використовувати імена типу "foo_t" - але я думаю, що вони потім зарезервовані POSIX.
Йоханнес Шауб - ліб

59
Отже, "толерантність" зарезервована POSIX, оскільки починається з "до" + малої літери? Я сумніваюся, що багато правил порушує це правило!
Sjoerd

23
@LokiAstari, " Стандарт C ++ визначається з точки зору стандарту C. В основному він говорить про те, що C ++ є C з цими відмінностями та доповненнями. " Дурниця! C ++ посилається лише на стандарт C у [basic.fundamental] та бібліотеці. Якщо те, що ви говорите, є істинним, то де C ++ це говорить, _Boolа _Imaginaryу C ++ не існує? Мова C ++ визначена явно, а не через "правки" на C, інакше стандарт може бути набагато коротшим!
Джонатан Уейклі

198

Правила уникнення зіткнення імен містяться як у стандарті C ++ (див. Книгу Stroustrup), так і згадуються гуруми C ++ (Sutter тощо).

Особисте правило

Оскільки я не хотів займатися справами і хотів просте правило, я створив особисте, яке є простим і правильним:

Іменуючи символ, ви уникнете зіткнення з компіляторами / ОС / стандартними бібліотеками, якщо:

  • ніколи не запускайте символ із підкресленням
  • ніколи не називайте символ із двома послідовними підкресленнями всередині.

Звичайно, введення коду в унікальний простір імен також допомагає уникнути зіткнення (але не захистить від злих макросів)

Деякі приклади

(Я використовую макроси, тому що вони більше забруднюють код символів C / C ++, але це може бути що завгодно, від назви змінної до назви класу)

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

Витяги з С ++ 0x чорнової

З файлу n3242.pdf (очікую, що остаточний стандартний текст буде подібним):

17.6.3.3.2 Глобальні імена [global.name]

Певні набори імен та підписів функцій завжди зарезервовані для реалізації:

- Кожне ім'я, яке містить подвійне підкреслення _ _ або починається з підкреслення, після якого велика літера (2.12) зарезервована для реалізації для будь-якого використання.

- Кожне ім'я, яке починається з підкреслення, зарезервоване для реалізації для використання в якості імені в глобальному просторі імен.

Але також:

17.6.3.3.5 Визначені користувачем буквальні суфікси [usrlit.suffix]

Буквальні ідентифікатори суфіксу, які не починаються з підкреслення, зарезервовані для подальшої стандартизації.

Цей останній пункт є заплутаним, якщо ви не вважаєте, що ім'я, що починається з однієї підкреслення, а за ним з малої літери, буде ОК, якщо не визначено у глобальному просторі імен ...


9
@Meysam: __WRONG_AGAIN__містить два послідовних підкреслення (два на початку та два в кінці), тому це неправильно відповідно до стандарту.
paercebal

8
@ BЈовић: WRONG__WRONGмістить два послідовних підкреслення (два посередині), тому це неправильно відповідно до стандарту
paercebal

2
введення коду в унікальну область імен також допомагає уникнути зіткнення : але цього все ж недостатньо, оскільки ідентифікатор може зіткнутися з ключовим словом незалежно від сфери застосування (наприклад, __attribute__для GCC).
Руслан

1
Чому виникає проблема мати два послідовних підкреслення в середині відповідно до стандарту? Визначені користувачем буквальні суфікси застосовуються до буквальних значень, таких як 1234567Lабо 4.0f; IIRC це стосується 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?Тому що стандарт каже, що вони зарезервовані. Це не порада щодо хорошого чи поганого стилю. Це рішення зі стандарту. Чому вони вирішили це? Я думаю, що перші компілятори вже використовували такі конвенції неофіційно перед стандартизацією.
paercebal

38

Від MSDN :

Використання двох послідовних символів підкреслення (__) на початку ідентифікатора, або одного провідного підкреслення, за яким йде велика літера, зарезервовано для реалізації C ++ у всіх областях. Вам слід уникати використання однієї провідної підкреслення з наступною малою літерою для імен із областю файлу через можливі конфлікти з поточними або майбутніми зарезервованими ідентифікаторами.

Це означає, що ви можете використовувати окремий підкреслення як префікс змінної члена, доки за ним слідує маленька літера.

Це, мабуть, взято з розділу 17.4.3.1.2 стандарту C ++, але я не можу знайти оригінальне джерело для повного стандарту в Інтернеті.

Дивіться також це питання .


2
Я знайшов подібний текст у n3092.pdf (проект стандарту C ++ 0x) у розділі: "17.6.3.3.2 Глобальні імена"
paercebal

7
Цікаво, що це, здається, єдина відповідь, яка має пряму, стислу відповідь на питання.
Гайд

9
@hyde: Насправді це не так, оскільки це пропускає правило не мати жодних ідентифікаторів з провідним підкресленням у глобальному просторі імен. Дивіться відповідь Роджера . Я б дуже насторожено ставився до цитат документів MS VC як авторитету стандарту C ++.
sbi

@sbi У цій відповіді я мав на увазі "ви можете використовувати одну підкреслення як префікс змінної члена до тих пір, поки за нею йде маленька літера" , яка відповідає на текст запитання безпосередньо та стисло, не затухаючи. в стіні тексту.
Гайд

5
По-перше, я все ще вважаю відсутність будь-якого натяку на те, що те саме правило не стосується глобального простору імен як провал. Що ще гірше, те, що сусідні підкреслення заборонено не тільки на початку, але і будь-де , в ідентифікаторі. Тож ця відповідь не просто опускає факт, але насправді висуває хоча б одне активно неправильне твердження. Як я вже говорив, посилатися на документи MSVC - це те, чого я б не робив, якщо тільки питання не стосується ВК.
sbi

25

Що стосується іншої частини питання, то звичайно ставити підкреслення в кінці назви змінної, щоб не зіткнутися з чимось внутрішнім.

Я це роблю навіть усередині класів та просторів імен, тому що мені потрібно запам’ятати лише одне правило (порівняно з «в кінці імені в глобальному масштабі та початку імені скрізь у всьому іншому»).


2

Так, підкреслення можуть використовуватися в будь-якому місці ідентифікатора. Я вважаю, що правила: будь-який з az, AZ, _ у першому символі та ті + 0-9 для наступних символів.

Префікси підкреслення звичайні в коді С - один підкреслення означає «приватне», а подвійні підкреслення зазвичай зарезервовані для використання компілятором.


3
Вони поширені в бібліотеках. Вони не повинні бути поширеними в коді користувача.
Мартін Йорк

43
Люди справді пишуть бібліотеки на С, ви знаєте.
Джон Міллікін

7
"Так, підкреслення можуть використовуватися в будь-якому місці ідентифікатора." Це неправильно для глобальних ідентифікаторів. Дивіться відповідь Роджера .
sbi
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.