¿Cuáles son las reglas sobre el uso de un guión bajo en un identificador de C ++?

931

Es común en C ++ nombrar variables miembro con algún tipo de prefijo para denotar el hecho de que son variables miembro, en lugar de variables o parámetros locales. Si has venido de un fondo MFC, probablemente lo usarás m_foo. También he visto myFooocasionalmente.

C # (o posiblemente solo .NET) parece recomendar usar solo un guión bajo, como en _foo. ¿Está permitido por el estándar C ++?

Roger Lipscombe
fuente
3
La página del manual de glibc sobre eso se puede encontrar en gnu.org/software/libc/manual/html_node/Reserved-Names.html Editar: ver también opengroup.org/onlinepubs/009695399/functions/xsh_chap02_02.html
CesarB
66
Solo para tener en cuenta que el desconocimiento de estas reglas no implica necesariamente que su código no se compilará o ejecutará, pero es probable que su código no sea portátil a diferentes compiladores y versiones, ya que no se puede garantizar que no habrá nombre choques Para respaldar esto, sé de cierta implementación de un sistema importante que ha estado utilizando como convención de nomenclatura la letra mayúscula en todas partes. Allí donde no hay errores debido a esto. Por supuesto que es una mala práctica.
g24l

Respuestas:

852

Las reglas (que no cambiaron en C ++ 11):

  • Reservado en cualquier ámbito, incluso para su uso como implementación macros de :
    • identificadores que comienzan con un guión bajo seguido inmediatamente por una letra mayúscula
    • identificadores que contienen guiones bajos adyacentes (o "guión bajo doble")
  • Reservado en el espacio de nombres global:
    • identificadores que comienzan con un guión bajo
  • Además, todo en el stdespacio de nombres está reservado. (Sin embargo, puede agregar especializaciones de plantilla).

Del 2003 C ++ Standard:

17.4.3.1.2 Nombres globales [lib.global.names]

Ciertos conjuntos de nombres y firmas de funciones siempre están reservados para la implementación:

  • Cada nombre que contiene un guión bajo doble ( __) o que comienza con un guión bajo seguido de una letra mayúscula (2.11) está reservado a la implementación para cualquier uso.
  • Cada nombre que comienza con un guión bajo está reservado a la implementación para su uso como nombre en el espacio de nombres global. 165

165) Tales nombres también están reservados en el espacio de nombres ::std(17.4.3.1).

Debido a que C ++ se basa en el estándar C (1.1 / 2, C ++ 03) y C99 es una referencia normativa (1.2 / 1, C ++ 03), esto también se aplica, desde el Estándar C de 1999:

7.1.3 Identificadores reservados

Cada encabezado declara o define todos los identificadores enumerados en su subcláusula asociada, y opcionalmente declara o define los identificadores enumerados en su subcláusula e identificadores de direcciones de biblioteca futuros asociados que siempre están reservados para cualquier uso o como identificadores de alcance de archivo.

  • Todos los identificadores que comienzan con un guión bajo y una letra mayúscula u otro guión bajo siempre están reservados para cualquier uso.
  • Todos los identificadores que comienzan con un guión bajo siempre están reservados para su uso como identificadores con alcance de archivo en los espacios de nombre ordinario y de etiqueta.
  • Cada nombre de macro en cualquiera de las siguientes subcláusulas (incluidas las futuras instrucciones de la biblioteca) está reservado para su uso tal como se especifica si se incluye alguno de sus encabezados asociados; a menos que se indique explícitamente lo contrario (ver 7.1.4).
  • Todos los identificadores con enlace externo en cualquiera de las siguientes subcláusulas (incluidas las futuras instrucciones de la biblioteca) siempre están reservados para su uso como identificadores con enlace externo. 154
  • Cada identificador con alcance de archivo enumerado en cualquiera de las siguientes subcláusulas (incluidas las futuras instrucciones de la biblioteca) está reservado para su uso como nombre de macro y como identificador con alcance de archivo en el mismo espacio de nombre si se incluye alguno de sus encabezados asociados.

No hay otros identificadores reservados. Si el programa declara o define un identificador en un contexto en el que está reservado (aparte de lo permitido por 7.1.4), o define un identificador reservado como un nombre de macro, el comportamiento es indefinido.

Si el programa elimina (con #undef ) cualquier definición de macro de un identificador en el primer grupo mencionado anteriormente, el comportamiento es indefinido.

154) la lista de identificadores reservados con enlace externo incluye errno, math_errhandling, setjmp, y va_end.

Se pueden aplicar otras restricciones. Por ejemplo, el estándar POSIX reserva muchos identificadores que pueden aparecer en el código normal:

  • Nombres que comienzan con mayúscula E siguen un dígito o una letra mayúscula:
    • puede usarse para nombres de códigos de error adicionales.
  • Nombres que comienzan con isoto seguidos de una letra minúscula
    • puede usarse para pruebas de caracteres adicionales y funciones de conversión.
  • Nombres que comienzan con LC_seguidos de una letra mayúscula
    • puede usarse para macros adicionales que especifican atributos locales.
  • Los nombres de todas las funciones matemáticas existentes con sufijo fo lestán reservados
    • para funciones correspondientes que operan en flotante y argumentos dobles largos, respectivamente.
  • Los nombres que comienzan SIGseguidos de una letra mayúscula están reservados.
    • para nombres de señal adicionales.
  • Nombres que comienzan con SIG_seguidos de una letra mayúscula están reservados.
    • para acciones de señal adicionales.
  • Nombres que comienzan con str, memowcs seguidos de una letra minúscula están reservados
    • para funciones adicionales de cadena y matriz.
  • Nombres que comienzan con PRIo SCNseguidos de cualquier letra minúscula oX están reservados
    • para macros de especificador de formato adicionales
  • Los nombres que terminan con _testán reservados
    • para nombres de tipo adicionales.

Si bien el uso de estos nombres para sus propios fines en este momento puede no causar un problema, aumentan la posibilidad de conflicto con futuras versiones de ese estándar.


Personalmente, simplemente no comienzo identificadores con guiones bajos. Nueva adición a mi regla: no use guiones bajos dobles en ningún lado, lo cual es fácil ya que rara vez uso guiones bajos.

Después de investigar este artículo, ya no termino con mis identificadores _t ya que está reservado por el estándar POSIX.

La regla sobre cualquier identificador que termina con _tme sorprendió mucho. Creo que es un estándar POSIX (aún no estoy seguro) en busca de aclaraciones y capítulos y versículos oficiales. Esto es del manual GNU libtool , que enumera los nombres reservados.

CesarB proporcionó el siguiente enlace a los símbolos y notas reservados POSIX 2004 "que muchos otros prefijos y sufijos reservados ... se pueden encontrar allí". Los símbolos reservados POSIX 2008 se definen aquí. Las restricciones son algo más matizadas que las anteriores.

Roger Pate
fuente
14
El estándar C ++ no "importa" el C, ¿verdad? Importan ciertos encabezados, pero no el lenguaje en su conjunto, o las reglas de nombres, que yo sepa. Pero sí, el _t uno también me sorprendió. Pero como es C, solo puede aplicarse a los ns globales. Debería ser seguro usar _t dentro de las clases mientras lo leo
jalf
27
El estándar C ++ no "importa" el estándar C. Hace referencia al estándar C. La introducción de la biblioteca C ++ dice "La biblioteca también pone a disposición las instalaciones de la Biblioteca Estándar C". Lo hace al incluir encabezados de la biblioteca estándar de C con los cambios apropiados, pero no "importándolos". El estándar C ++ tiene un conjunto de reglas propio que describe los nombres reservados. Si un nombre reservado en C debe reservarse en C ++, ese es el lugar para decirlo. Pero el estándar C ++ no lo dice. Así que no creo que las cosas reservadas en C estén reservadas en C ++, pero podría estar equivocado.
Johannes Schaub - litb
8
Esto es lo que encontré sobre el problema "_t": n1256 (C99 TC3) dice: "Los nombres de typedef que comienzan con int o uint y terminan con _t" están reservados. Creo que todavía permite usar nombres como "foo_t", pero creo que estos son reservados por POSIX.
Johannes Schaub - litb
59
Entonces, ¿POSIX reserva 'tolerancia' ya que comienza con 'a' + una letra minúscula? ¡Apuesto a que muchos códigos rompen esta regla!
Sjoerd
23
@LokiAstari, " El estándar C ++ se define en términos del estándar C. Básicamente dice que C ++ es C con estas diferencias y adiciones " . ¡Tonterías! C ++ solo hace referencia al estándar C en [basic.fundamental] y la biblioteca. Si lo que dices es cierto, ¿dónde dice eso C ++ _Booly _Imaginaryno existe en C ++? El lenguaje C ++ se define explícitamente, no en términos de "ediciones" a C, de lo contrario, el estándar podría ser mucho más corto.
Jonathan Wakely
198

Las reglas para evitar la colisión de nombres están en el estándar C ++ (ver el libro de Stroustrup) y mencionadas por los gurús de C ++ (Sutter, etc.).

Regla personal

Debido a que no quería tratar casos, y quería una regla simple, he diseñado una regla personal que es simple y correcta:

Al nombrar un símbolo, evitará la colisión con el compilador / OS / bibliotecas estándar si:

  • nunca comience un símbolo con un guión bajo
  • nunca nombre un símbolo con dos guiones bajos consecutivos dentro.

Por supuesto, poner su código en un espacio de nombre único también ayuda a evitar colisiones (pero no protegerá contra las macros malvadas)

Algunos ejemplos

(Uso macros porque son más contaminantes de código de símbolos C / C ++, pero podría ser cualquier cosa, desde el nombre de la variable hasta el nombre de la clase)

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

Extractos del borrador de C ++ 0x

Del archivo n3242.pdf (espero que el texto estándar final sea similar):

17.6.3.3.2 Nombres globales [global.names]

Ciertos conjuntos de nombres y firmas de funciones siempre están reservados para la implementación:

- Cada nombre que contiene un guión bajo doble _ _ o comienza con un guión bajo seguido de una letra mayúscula (2.12) está reservado a la implementación para cualquier uso.

- Cada nombre que comienza con un guión bajo está reservado a la implementación para su uso como nombre en el espacio de nombres global.

Pero también:

17.6.3.3.5 Sufijos literales definidos por el usuario [usrlit.suffix]

Los identificadores de sufijo literales que no comienzan con un guión bajo están reservados para la estandarización futura.

Esta última cláusula es confusa, a menos que considere que un nombre que comience con un guión bajo y seguido de una letra minúscula estaría bien si no se define en el espacio de nombres global ...

paercebal
fuente
99
@Meysam: __WRONG_AGAIN__contiene dos guiones bajos consecutivos (dos al principio y dos al final), por lo que esto está mal de acuerdo con el estándar.
paercebal
8
@ BЈовић: WRONG__WRONGcontiene dos guiones bajos consecutivos (dos en el medio), por lo que esto está mal de acuerdo con el estándar
paercebal
2
poner su código en un espacio de nombre único también ayuda a evitar la colisión : pero esto aún no es suficiente, ya que el identificador puede colisionar con una palabra clave independientemente del alcance (por ejemplo, __attribute__para GCC).
Ruslan
1
¿Por qué hay algún problema de tener dos guiones bajos consecutivos en el medio de acuerdo con el estándar? Los sufijos literales definidos por el usuario se aplican a valores literales como 1234567Lo 4.0f; IIRC se refiere a 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?Porque la norma dice que están reservados. Este no es un consejo sobre el estilo bueno o malo. Es una decisión del estándar. ¿Por qué decidieron esto? Supongo que los primeros compiladores ya usaban tales convenciones de manera informal antes de la estandarización.
paercebal
38

De MSDN :

El uso de dos caracteres de subrayado secuencial (__) al comienzo de un identificador, o un único guión bajo seguido de una letra mayúscula, está reservado para implementaciones de C ++ en todos los ámbitos. Debe evitar usar un guión bajo seguido de una letra minúscula para los nombres con alcance de archivo debido a posibles conflictos con identificadores reservados actuales o futuros.

Esto significa que puede usar un solo guión bajo como prefijo de variable miembro, siempre que vaya seguido de una letra minúscula.

Aparentemente, esto se toma de la sección 17.4.3.1.2 del estándar C ++, pero no puedo encontrar una fuente original para el estándar completo en línea.

Ver también esta pregunta .

Roger Lipscombe
fuente
2
Encontré un texto similar en n3092.pdf (el borrador del estándar C ++ 0x) en la sección: "17.6.3.3.2 Nombres globales"
paercebal
77
Curiosamente, esta parece ser la única respuesta que tiene una respuesta directa y concisa a la pregunta.
hyde
99
@hyde: En realidad, no lo es, ya que se está omitiendo la regla de no tener ningún identificador con un guión bajo en el espacio de nombres global. Ver la respuesta de Roger . Sería muy cuidadoso con las citas de documentos de MS VC como autoridad en el estándar C ++.
sbi
@sbi Me refería a "puede usar un solo guión bajo como prefijo de variable de miembro, siempre que esté seguido de una letra minúscula" en esta respuesta, que responde a la pregunta en el texto de la pregunta de manera directa y concisa, sin ahogarse en una pared de texto
hyde
55
Primero, todavía considero que la falta de cualquier pista de que la misma regla no se aplica al espacio de nombres global es un fracaso. Sin embargo, lo que es peor es que los guiones bajos adyacentes están prohibidos no solo al comienzo de un identificador , sino en cualquier parte de este. Entonces, esta respuesta no es simplemente omitir un hecho, sino que en realidad hace al menos un reclamo activamente incorrecto. Como dije, referirme a los documentos de MSVC es algo que no haría a menos que la pregunta sea únicamente sobre VC.
sbi
25

En cuanto a la otra parte de la pregunta, es común poner el guión bajo al final del nombre de la variable para no chocar con nada interno.

Hago esto incluso dentro de las clases y espacios de nombres porque solo tengo que recordar una regla (en comparación con "al final del nombre en el ámbito global y el comienzo del nombre en cualquier otro lugar").

Max Lybbert
fuente
2

Sí, los guiones bajos se pueden usar en cualquier parte de un identificador. Creo que las reglas son: cualquiera de az, AZ, _ en el primer carácter y aquellos + 0-9 para los siguientes caracteres.

Los prefijos de subrayado son comunes en el código C: un solo guión bajo significa "privado", y los subrayados dobles generalmente están reservados para el compilador.

John Millikin
fuente
3
Son comunes en las bibliotecas. No deberían ser comunes en el código de usuario.
Martin York
43
Las personas hacen las bibliotecas de escritura en C, ya sabes.
John Millikin el
77
"Sí, los guiones bajos se pueden usar en cualquier parte de un identificador". Esto está mal para los identificadores globales. Ver la respuesta de Roger .
sbi