В C++ принято называть переменные-члены каким-либо префиксом, чтобы обозначить тот факт, что они являются переменными-членами, а не локальными переменными или параметрами. Если у вас есть опыт работы с MFC, вы, вероятно, будете использовать m_foo. Иногда я видел myFoo.
C# (или, возможно, просто .NET), кажется, рекомендует использовать только подчеркивание, как в _foo. Разрешено ли это стандартом C++?
Просто отметим, что незнание этих правил не обязательно означает, что ваш код не будет компилироваться или запускаться, но вполне вероятно, что ваш код не будет переноситься на другие компиляторы и версии, поскольку нельзя гарантировать, что не будет имени столкновения. Чтобы подтвердить это, я знаю определенную реализацию важной системы, в которой в качестве соглашения об именах везде используется заглавная буква _. Никаких ошибок из-за этого нет. Конечно, это плохая практика.





Да, символы подчеркивания могут использоваться в любом месте идентификатора. Я считаю, что правила следующие: любой из букв a-z, A-Z, _ в первом символе и + 0-9 для следующих символов.
Префиксы подчеркивания распространены в коде C - одиночное подчеркивание означает «частный», а двойное подчеркивание обычно зарезервировано для использования компилятором.
Они распространены в библиотеках. Они не должны быть обычными в пользовательском коде.
Знаете, люди делать пишут библиотеки на C.
@sbi В соответствии со стандартами C и C++, да, семантически глобальные идентификаторы с ведущими символами подчеркивания зарезервированы. Однако они являются синтаксически допустимыми идентификаторами, и компилятор не помешает вам назвать функцию _Foo, хотя при этом вы полагаетесь на нестандартные детали реализации и, таким образом, рискуете, что ваш код будет нарушен будущими версиями реализации языка / стандартной библиотеки. /ОПЕРАЦИОННЫЕ СИСТЕМЫ.
@BenW: TTBOMK, стандарт C++ просто говорит, что глобальные идентификаторы, начинающиеся с символа подчеркивания, недопустимы, без какого-либо различия между синтаксисом и семантикой. (Также любые идентификаторы, начинающиеся с символа подчеркивания, за которым следует заглавная буква, и идентификаторы с двумя последовательными символами подчеркивания.)
От MSDN:
Use of two sequential underscore characters ( __ ) at the beginning of an identifier, or a single leading underscore followed by a capital letter, is reserved for C++ implementations in all scopes. You should avoid using one leading underscore followed by a lowercase letter for names with file scope because of possible conflicts with current or future reserved identifiers.
Это означает, что вы можете использовать одиночный символ подчеркивания в качестве префикса переменной-члена, если за ним следует строчная буква.
Очевидно, это взято из раздела 17.4.3.1.2 стандарта C++, но я не могу найти исходный источник полного стандарта в Интернете.
См. Также этот вопрос.
Я нашел похожий текст в n3092.pdf (черновик стандарта C++ 0x) в разделе: «17.6.3.3.2 Глобальные имена»
Интересно, что это, кажется, единственный ответ, который дает прямой и лаконичный ответ на вопрос.
@hyde: На самом деле это не так, поскольку он пропускает правило, чтобы не иметь никаких идентификаторов с ведущим подчеркиванием в глобальном пространстве имен. См. Ответ Роджера. Я бы очень осторожно относился к ссылкам на документы MS VC как на авторитетные источники по стандарту C++.
@sbi В этом ответе я имел в виду "вы можете использовать одиночный символ подчеркивания в качестве префикса переменной-члена, если за ним следует строчная буква", который отвечает на вопрос по тексту вопроса прямо и кратко, не утопая в стене текста.
Во-первых, я до сих пор считаю неудачей отсутствие каких-либо намеков на то, что то же правило не применяется к глобальному пространству имен. Что еще хуже, так это то, что соседние символы подчеркивания запрещены не только в начале, но и в везде в идентификаторе. Таким образом, этот ответ не просто пропускает факт, но фактически делает по крайней мере одно явно неверное утверждение. Как я уже сказал, ссылаться на документы MSVC - это то, чего я бы не стал делать, если только вопрос не касается исключительно VC.
@sbi: Правило внутреннего двойного подчеркивания было разработано, чтобы зарезервировать такие идентификаторы для имен с измененным типом, но я думаю, что имена с двойным подчеркиванием можно было бы приспособить, сказав, что любые вхождения __, сгенерированные искажением на основе типов, будут __x , а затем заявив, что любые вхождения __ в указанном имени будут заменены на __y перед таким искажением.
А как насчет одного подчеркивания как полного имени переменной-члена?
@sbi Есть ирония в том, что VC соответствует стандарту ISO C++, который также резервирует имена с одним подчеркиванием, одновременно переименовывая некоторые функции posix, например _dup () вместо dup ()
Правила (которые не изменились в C++ 11):
std зарезервировано. (Тем не менее, вам разрешено добавлять специализации шаблонов.)Из стандарта C++ 2003:
17.4.3.1.2 Global names [lib.global.names]
Certain sets of names and function signatures are always reserved to the implementation:
- Each name that contains a double underscore (
__) or begins with an underscore followed by an uppercase letter (2.11) is reserved to the implementation for any use.- Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.165
165) Such names are also reserved in namespace
::std(17.4.3.1).
Поскольку C++ основан на стандарте C (1.1 / 2, C++ 03), а C99 является нормативной ссылкой (1.2 / 1, C++ 03), они также применяются из стандарта C 1999 года:
7.1.3 Reserved identifiers
Each header declares or defines all identifiers listed in its associated subclause, and optionally declares or defines identifiers listed in its associated future library directions subclause and identifiers which are always reserved either for any use or for use as file scope identifiers.
- All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.
- All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces.
- Each macro name in any of the following subclauses (including the future library directions) is reserved for use as specified if any of its associated headers is included; unless explicitly stated otherwise (see 7.1.4).
- All identifiers with external linkage in any of the following subclauses (including the future library directions) are always reserved for use as identifiers with external linkage.154
- Each identifier with file scope listed in any of the following subclauses (including the future library directions) is reserved for use as a macro name and as an identifier with file scope in the same name space if any of its associated headers is included.
No other identifiers are reserved. If the program declares or defines an identifier in a context in which it is reserved (other than as allowed by 7.1.4), or defines a reserved identifier as a macro name, the behavior is undefined.
If the program removes (with
#undef) any macro definition of an identifier in the first group listed above, the behavior is undefined.154) The list of reserved identifiers with external linkage includes
errno,math_errhandling,setjmp, andva_end.
Могут применяться другие ограничения. Например, стандарт POSIX резервирует множество идентификаторов, которые могут отображаться в обычном коде:
E, следует за цифрой или заглавной буквой:
is или to, за которыми следует строчная буква
LC_, за которыми следует заглавная буква
f или l зарезервированы.
SIG, за которыми следует заглавная буква, зарезервированы.
SIG_, за которыми следует заглавная буква, зарезервированы.
str, mem или wcs, за которыми следует строчная буква, зарезервированы.
PRI или SCN, за которыми следует любая строчная буква или X, зарезервированы.
_t, зарезервированы.
Хотя использование этих имен в ваших собственных целях прямо сейчас может не вызвать проблем, они все же повышают вероятность конфликта с будущими версиями этого стандарта.
Лично я просто не начинаю идентификаторы с подчеркивания. Новое дополнение к моему правилу: нигде не используйте двойные подчеркивания, что легко, поскольку я редко использую подчеркивание.
После исследования этой статьи я больше не заканчиваю свои идентификаторы на _t.
поскольку это зарезервировано стандартом POSIX.
Правило о любом идентификаторе, заканчивающемся на _t, меня очень удивило. Я думаю, что это стандарт POSIX (пока не уверен), требующий разъяснений и официальных глав и стихов. Это из Руководство GNU libtool, перечисляющего зарезервированные имена.
CesarB предоставил следующую ссылку на зарезервированные символы POSIX 2004 и отмечает, что «там можно найти многие другие зарезервированные префиксы и суффиксы ...». В Здесь определены зарезервированные символы POSIX 2008. Ограничения несколько более тонкие, чем указанные выше.
Просто примечание - за исключением нумерации, то, что Мартин процитировал из черновика стандарта, в точности совпадает со стандартом C++ 03 (17.4.3.1.2).
В вашем резюме не говорится того же, что и в цитате из Стандарта.
Имена Глобальный отличаются от "любого идентификатора
@Adam Mitz: Глобальные имена также охватывают МАКРОСЫ. Что превратит ваши идентификаторы в кашу. Это то, что я пытался передать.
Вот официальная глава и стих, пожалуйста, добавьте к своему уже отличному ответу: opengroup.org/onlinepubs/009695399/functions/xsh_chap02_02.h tml (и обратите внимание, что там можно найти множество других зарезервированных префиксов и суффиксов, которые вы не упомянули).
Стандарт C++ не «импортирует» C, не так ли? Насколько мне известно, они импортируют определенные заголовки, но не язык в целом или правила именования. Но да, этот тоже меня удивил. Но поскольку это C, он может применяться только к глобальным ns. Должно быть безопасно использовать _t внутри классов, когда я его читал
@jalf: Стандарт C++ определяется в терминах стандарта C. По сути, он говорит, что C++ - это C с этими отличиями и дополнениями.
Мартин, в ответ вы говорите: «По крайней мере, это означает, что они не являются макросами ...», что я читал как «глобальные имена не являются макросами», что я также считаю, что это не так. макросы не являются членами :: и, следовательно, не являются глобальными. но в комментарии вы говорите, что «глобальные имена также охватывают МАКРОСЫ».
каково ваше окончательное мнение по этому поводу? Я видел, как вы добавили это в ответ после того, как оставили свой комментарий. Итак, вы того же мнения, что и я, о том, что макросы не являются глобальными именами?
Где в стандарте C++ проводится различие между вещами, зарезервированными «для компилятора» и «зарезервированными для ОС и библиотек», пожалуйста? Я видел, где зарезервированы имена для реализации, но не где указывается какое-либо различие между «компилятором», «ОС» и «библиотеками» как компонентами реализации.
Стандарт C++ не «импортирует» стандарт C. Это использованная литература Стандарт C. Во введении к библиотеке C++ говорится: «Библиотека также предоставляет возможности стандартной библиотеки C». Это достигается путем включения заголовков стандартной библиотеки C с соответствующими изменениями, но не путем ее «импорта». Стандарт C++ имеет собственный набор правил, описывающих зарезервированные имена. Если имя, зарезервированное в C, должно быть зарезервировано в C++, это место, где можно сказать это. Но в стандарте C++ об этом не говорится. Поэтому я не верю, что вещи, зарезервированные в C, зарезервированы в C++, но я вполне могу ошибаться.
Вот что я обнаружил о проблеме «_t»: n1256 (C99 TC3) говорит: «Имена Typedef, начинающиеся с int или uint и заканчивающиеся на _t» зарезервированы. Я думаю, что это все еще позволяет использовать такие имена, как "foo_t", но я думаю, что они затем зарезервированы POSIX.
Из стандарта C++ 1.1. <quote> C++ - это язык программирования общего назначения, основанный на языке программирования C, как описано в ISO / IEC 9899: 1990 Языки программирования - C (1.2). В дополнение к возможностям, предоставляемым C, C++ предоставляет дополнительные типы данных </quote>. Я считаю, что все, что зарезервировано в C, также зарезервировано в C++, если иное явно не указано.
Как отмечалось в основной статье, суффикс _t зарезервирован только стандартом POSIX, а не стандартом C.
Значит, «толерантность» зарезервирована POSIX, поскольку начинается с «до» + строчная буква? Бьюсь об заклад, это правило нарушает большое количество кода!
@Sjoerd: Наверное. Хотя я уверен, что у вас все будет хорошо, если lerance не станет настоящим глаголом, который можно применять к персонажам. Также обратите внимание, что он зарезервирован только в глобальной области (C) _или стандартном пространстве имен (C++), поэтому вы можете иметь функциональные переменные с этим именем, не нарушая правила.
@ReubenMorais: Нет. Прочтите документацию по Posix.
GNU getopt_long() нарушает все правила: он определяет макросы no_argument, required_argument и optional_argument.
@MaximYegorushkin: Никаких правил не нарушено. Эти идентификаторы зарезервированы для реализации. getopt_long () является частью реализации компиляторов и стандартных библиотек GNU.
@LokiAstari, "Стандарт C++ определяется в терминах стандарта C. По сути, он говорит, что C++ - это C с этими отличиями и дополнениями." Ерунда! C++ ссылается только на стандарт C в [basic.fundamental] и библиотеке. Если то, что вы говорите, правда, где C++ говорит, что _Bool и _Imaginary не существуют в C++? Язык C++ определяется явно, а не в терминах «правок» C, иначе стандарт мог бы быть намного короче!
@JonathanWakely: Я имел в виду второй абзац стандарта: <quote> C++ - это язык программирования общего назначения основан на языке программирования C, как описано в ISO / IEC 9899: 1999 Языки программирования - C (далее - стандарт C). В дополнение к возможностям, предоставляемым C, C++ предоставляет дополнительные типы данных, классы, шаблоны, исключения, пространства имен, перегрузку операторов, перегрузку имен функций, ссылки, бесплатные операторы управления хранилищами и дополнительные возможности библиотеки. </quote>
Если вы интерпретируете мое вышеупомянутое утверждение как нечто иное, приношу свои извинения за неточность.
@LokiAstari, это очень общее утверждение, описывающее объем языка, это не означает, что все, что есть в C, импортируется в C++. Язык C++ (не библиотека) точно определяется своим собственным стандартом, а не ссылкой на другой, за исключением одной ссылки в [basic.fundamental].
@JonathanWakely: <quote> В дополнение к возможностям, предоставляемым C, C++ предоставляет дополнительные .... </quote>. Но вы также должны воспринимать комментарий в контексте обсуждения в целом. Мы говорим о «зарезервированных именах» или, в частности, о «подчеркивании». Таким образом, я пытался передать, что зарезервированные имена в C также зарезервированы в C++. litb не согласен с такой интерпретацией, и я знаю, что он очень внимательно читает стандарт. Но этот разговор разрешился больше года назад.
В C++ я вижу только [lex.name] и для глобальных имен [global.names]. Можете ли вы объяснить, как тот факт, что C++ основан на стандарте C, а C99 является нормативным справочником, позволяет применять правила C99 к C++. Благодарность
см. [intro.refs] из стандарта, он описывает, что это значит. См. здесь, чтобы получить копию
@LokiAstari: Я думаю, что ваше утверждение неверно. Одна из возможностей языка C состоит в том, что вы можете использовать идентификаторы, зарезервированные для него не; поэтому, если мы собираемся считать буквально уместным, что C++ включает «возможности, предоставляемые C», тогда идентификаторы, зарезервированные C++, фактически должны быть (максимум) подмножество из тех, что зарезервированы C, а не суперсет. (Но на самом деле мы с вами оба знаем, что C++ зарезервировал некоторые идентификаторы, которых нет в C, поэтому, очевидно, выражение «средства, предоставляемые C» буквально релевантно.)
@ruakh: Выше привожу цитату из стандарта C. 7.1.3 Reserved identifiers. Пожалуйста, перечитайте.
@LokiAstari: Проблема в том, в чем был бы вопрос? «Оставляет ли C++ неопределенным все, что остается неопределенным в C?» слишком тенденциозен (я полагаю, вы не можете далеко уйти от который), тогда как «Все ли идентификаторы зарезервированы в C, также зарезервированы в C++?» будет закрыто как обман этого. Должен ли я просто процитировать раздел спецификации C++, который вы цитируете, и спросить, каковы его нормативные последствия?
Может быть полезно знать, что большинство зарезервированных символов POSIX зарезервированы только при включении соответствующего включаемого файла, т.е. "int stringptr" является "допустимым", пока вы не включите <string.h>.
@LokiAstari Я понимаю, что такие стандарты необходимы для C++. Но, например, в Java есть только несколько зарезервированных имен полей (например, serialVersionUID), и, конечно, никакие стандарты, такие как переменные, заканчивающиеся на _t, не зарезервированы, потому что язык был разработан таким образом, что все находится в пространстве имен. Вы хотите сказать, что любой язык, который может быть скомпилирован в машинный код на нескольких платформах, должен иметь эти зарезервированные стандарты имен переменных?
Интересно, возникнет ли какая-либо проблема с указанием того, что конкретный префикс был зарезервирован для макросов, определенных в будущих языковых версиях, с условием, что реализации должны либо обрабатывать их в соответствии со стандартом C, либо оставлять их неопределенными. Это позволило бы коду, использующему определенные новые функции, работать со старыми компиляторами, определяя макросы для их эмуляции. Например, если __CPP_EITHER (x, y) принимает два выражения или утверждения и позволяет компилятору выбирать между ними произвольным образом (надеюсь, в зависимости от того, какие из них могут быть скомпилированы более эффективно), то ...
... код, использующий эту директиву, может работать с существующими реализациями, просто #ifndef __CPP_EITHER / #define __CPP_EITHER(x,y) x / #endif, но реализация, которая понимает директиву, может использовать ее для улучшения генерации кода в тех случаях, когда она может сказать, что y будет более эффективным, чем x (в случаях там, где он не мог сказать, он мог просто использовать x).
@Sjoerd примерно да. В нем говорится, что любая реализация может определить новую функцию ctype tofoo для любого идентификатора foo, начинающегося с буквы, включая lerance. Если это произойдет, и это вызовет конфликт с вашим собственным глобалом, ну, вас предупредили. Практическое влияние на ты невелико, но это дает POSIX и разработчикам передышку, чтобы добавлять вещи без бесконечных придирок.
Правила могут быть лучше обновлены, чтобы отразить тот факт, что правила зарезервированных имен перемещены из библиотеки (раздел 17) на базовый язык (раздел 2) в текущем рабочем проекте стандарта C++.
Следует отметить, что компиляторы не будут проверять, нарушены ли какие-либо эти правила резервирования, поэтому, если они используются в коде, он может работать сегодня, но сломаться (потенциально незаметно) при следующем применении какого-нибудь безобидного кажущегося обновления или исправления.
Если эти правила нарушены, вызывает ли это неопределенное поведение?
@MaxBarraclough Да. Что может означать, что ничего не происходит. См. Раздел 5.10 Идентификаторы. Пункт 3In addition, some identifiers are reserved for use by C++ implementations and **shall not be used otherwise**; no diagnostic is required.
@MaxBarraclough Здесь важный термин - Не должны. Если вы посмотрите на C++ Раздел 3 Термины и определенияFor the purposes of this document, the terms and definitions given in ISO/IEC 2382-1:1993, the terms, definitions, and symbols given in ISO 80000-2:2009, and the following apply., вы можете найти эти термины здесь: iso.org/obp/ui => требуется, чтобы не.
@MaxBarraclough Таким образом, если вы нарушите это условие, ваш код не соответствует требованиям. Если затем прочитать Раздел 4 Общие принципы пункт 2.3If a program contains a violation of a rule for which no diagnostic is required, this document places no requirement on implementations with respect to that program.
@MaxBarraclough И напоследок. Смотрим на Раздел 3.27 неопределенное поведениеbehavior for which this document imposes no requirements.
@MartinYork: Каковы требования к соответствующей программе на языке C? В каждой версии Стандарта, которую я видел, нарушение ограничения означало бы, что программа не соответствует строго, но реализациям разрешено документировать расширения, которые отказываются от ограничений, и программа, которая работает в такой реализации, будет соответствовать, даже если это нарушает ограничение.
@supercat Я редко использую C, поэтому не знаю.
@MartinYork: Стандарт C++ определяет понятие производительности для программы или просто реализации? Мне кажется, я припоминаю, что в прологе говорится, что любая ссылка на то, что программы могут или не могут делать, предназначена исключительно для интерпретации в отношении требований к реализациям.
@MartinYork: Это различие важно, потому что реализациям разрешено расширять язык таким образом, чтобы расширять диапазон программ, которые они могут эффективно обрабатывать, и такое расширение может включать программы, которые нарушают ограничения. Нарушение ограничения не делает программу несоответствующей (поскольку такой концепции нет), но вместо этого означает, что реализациям не нужно осмысленно обрабатывать программу, если они не хотят этого делать.
@supercat Почему вы спрашиваете в комментариях (это не подходящее место для этого обсуждения). Похоже, вам стоит задать это как вопрос. Тогда люди со знаниями попытаются ответить.
Я не верю, что это прописано в стандарте, но всегда ли идентификатор с «тройным подчеркиванием» (___) считается имеющим двойное подчеркивание? Я ... верю, что должен? Но эмпирические данные с моей стороны показывают, что некоторые люди могут найти приемлемым тройное подчеркивание.
Но на самом деле формулировка такова: Each identifier that contains a double underscore __ Если у вас тройка, она содержит двойную!
Правила, позволяющие избежать столкновения имен, содержатся как в стандарте C++ (см. Книгу Страуструпа), так и упоминаются гуру C++ (Саттер и т. д.).
Поскольку я не хотел заниматься случаями и хотел иметь простое правило, я разработал личный, которое одновременно является простым и правильным:
При именовании символа вы избежите столкновения с компилятором / ОС / стандартными библиотеками, если вы:
Конечно, размещение вашего кода в уникальном пространстве имен тоже помогает избежать коллизий (но не защитит от злых макросов).
(Я использую макросы, потому что они больше загрязняют код символов C / C++, но это может быть что угодно, от имени переменной до имени класса)
#define _WRONG
#define __WRONG_AGAIN
#define RIGHT_
#define WRONG__WRONG
#define RIGHT_RIGHT
#define RIGHT_x_RIGHT
Из файла n3242.pdf (я ожидаю, что окончательный стандартный текст будет аналогичным):
17.6.3.3.2 Global names [global.names]
Certain sets of names and function signatures are always reserved to the implementation:
— Each name that contains a double underscore _ _ or begins with an underscore followed by an uppercase letter (2.12) is reserved to the implementation for any use.
— Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.
Но и:
17.6.3.3.5 User-defined literal suffixes [usrlit.suffix]
Literal suffix identifiers that do not start with an underscore are reserved for future standardization.
Это последнее предложение сбивает с толку, если вы не считаете, что имя, начинающееся с одного подчеркивания и сопровождаемое строчной буквой, будет ОК, если нет определено в глобальном пространстве имен ...
@Meysam: __WRONG_AGAIN__ содержит два последовательных символа подчеркивания (два в начале и два в конце), поэтому согласно стандарту это неверно.
@ BЈовић: WRONG__WRONG содержит два последовательных символа подчеркивания (два посередине), поэтому согласно стандарту это неверно.
__attribute__ для GCC).
Почему возникает проблема наличия двух последовательных символов подчеркивания в центре в соответствии со стандартом? Определяемые пользователем буквальные суффиксы применяются к буквальным значениям, таким как 1234567L или 4.0f; IIRC это относится к http://en.cppreference.com/w/cpp/language/user_literal
Why is there any problem of having two consecutive underscores in the middle according to the standard? Потому что стандарт гласит, что они зарезервированы. Это не совет в хорошем или плохом стиле. Это решение из стандарта. Почему они так решили? Я полагаю, что первые компиляторы уже неформально использовали такие соглашения до стандартизации.
Мне не удалось найти предложение [global.names] или что-то подобное в текущем проекте стандарта (eel.is/c++draft). Вроде убрали.
@paercebal изначально это было сделано для того, чтобы у компиляторов всегда был простой способ изменять имена. В наше время это не так полезно, но сохранено для обратной совместимости.
@CoffeeTableEspresso: я все еще не понимаю, почему любая реализация требует, чтобы имена исходного кода не содержали двойных подчеркиваний. Даже если существующая реализация экспортировала сами имена с двойным подчеркиванием и запретила их в исходном коде, такая реализация могла бы добавить поддержку таких имен без нарушения совместимости компоновщика с любыми существующими объектными файлами, например, указание, что любой запуск из N символов подчеркивания в имени исходного кода будет заменен на 2N + 1 подчеркивание в имени компоновщика.
Что касается другой части вопроса, обычно в конец имени переменной ставится подчеркивание, чтобы не конфликтовать ни с чем внутренним.
Я делаю это даже внутри классов и пространств имен, потому что тогда мне нужно запомнить только одно правило (по сравнению с «в конце имени в глобальной области и в начале имени везде»).
Страницу руководства glibc об этом можно найти в gnu.org/software/libc/manual/html_node/Reserved-Names.html Edit: см. Также opengroup.org/onlinepubs/009695399/functions/xsh_chap02_02.h tml