Quais são as regras sobre o uso de sublinhado em um identificador C ++?

931

É comum no C ++ nomear variáveis ​​de membros com algum tipo de prefixo para indicar o fato de que são variáveis ​​de membros, em vez de variáveis ​​ou parâmetros locais. Se você é do MFC, provavelmente usará m_foo. Eu também já vi myFooocasionalmente.

C # (ou possivelmente apenas .NET) parece recomendar o uso de apenas um sublinhado, como em _foo. Isso é permitido pelo padrão C ++?

Roger Lipscombe
fonte
3
A página do manual glibc sobre isso podem ser encontradas em gnu.org/software/libc/manual/html_node/Reserved-Names.html Edit: ver também opengroup.org/onlinepubs/009695399/functions/xsh_chap02_02.html
CesarB
6
Apenas para observar que a ignorância dessas regras não implica necessariamente que seu código não será compilado ou executado, mas é provável que ele não seja portátil para diferentes compiladores e versões, pois não é possível garantir que não haverá nome. confrontos. Para apoiar isso, conheço certa implementação de um sistema importante que tem usado como convenção de nomenclatura a letra maiúscula em todos os lugares. Lá onde não há erros devido a isso. Claro que é uma má prática.
G24l

Respostas:

852

As regras (que não foram alteradas no C ++ 11):

  • Reservado em qualquer escopo, inclusive para uso como macros de implementação :
    • identificadores começando com um sublinhado seguido imediatamente por uma letra maiúscula
    • identificadores que contêm sublinhados adjacentes (ou "sublinhado duplo")
  • Reservado no espaço para nome global:
    • identificadores começando com um sublinhado
  • Além disso, tudo no stdespaço para nome é reservado. (Você tem permissão para adicionar especializações de modelo.)

No padrão C ++ 2003:

17.4.3.1.2 Nomes globais [lib.global.names]

Certos conjuntos de nomes e assinaturas de funções são sempre reservados para a implementação:

  • Cada nome que contém um sublinhado duplo ( __) ou começa com um sublinhado seguido por uma letra maiúscula (2.11) é reservado à implementação para qualquer uso.
  • Cada nome que começa com um sublinhado é reservado à implementação para uso como um nome no espaço para nome global. 165

165) Esses nomes também são reservados no espaço para nome ::std(17.4.3.1).

Como o C ++ é baseado no padrão C (1.1 / 2, C ++ 03) e o C99 é uma referência normativa (1.2 / 1, C ++ 03), isso também se aplica, no Padrão C de 1999:

7.1.3 Identificadores reservados

Cada cabeçalho declara ou define todos os identificadores listados em sua sub-cláusula associada e, opcionalmente, declara ou define identificadores listados em sua sub-cláusula e identificadores de direções futuras da biblioteca associadas, sempre reservados para qualquer uso ou como identificadores de escopo do arquivo.

  • Todos os identificadores que começam com um sublinhado e uma letra maiúscula ou outro sublinhado são sempre reservados para qualquer uso.
  • Todos os identificadores que começam com um sublinhado são sempre reservados para uso como identificadores com escopo de arquivo nos espaços de nomes comuns e de marca.
  • Cada nome de macro em qualquer uma das subcláusulas a seguir (incluindo as futuras instruções da biblioteca) é reservado para uso conforme especificado, se qualquer um de seus cabeçalhos associados for incluído; salvo indicação explícita em contrário (ver 7.1.4).
  • Todos os identificadores com ligação externa em qualquer uma das subcláusulas a seguir (incluindo as instruções futuras da biblioteca) são sempre reservados para uso como identificadores com ligação externa. 154
  • Cada identificador com escopo de arquivo listado em qualquer uma das subcláusulas a seguir (incluindo as instruções futuras da biblioteca) é reservado para uso como um nome de macro e como um identificador com escopo de arquivo no mesmo espaço de nome, se algum dos cabeçalhos associados estiver incluído.

Nenhum outro identificador está reservado. Se o programa declarar ou definir um identificador em um contexto no qual está reservado (exceto como permitido pelo 7.1.4), ou definir um identificador reservado como um nome de macro, o comportamento será indefinido.

Se o programa remover (com #undef) qualquer definição de macro de um identificador no primeiro grupo listado acima, o comportamento será indefinido.

154) A lista de identificadores reservados com ligação externa inclui errno, math_errhandling, setjmp, e va_end.

Outras restrições podem ser aplicadas. Por exemplo, o padrão POSIX reserva muitos identificadores que provavelmente aparecerão no código normal:

  • Os nomes que começam com maiúsculas Eseguem uma letra maiúscula ou um dígito:
    • pode ser usado para nomes de códigos de erro adicionais.
  • Nomes que começam com isou são toseguidos por uma letra minúscula
    • pode ser usado para testes adicionais de caracteres e funções de conversão.
  • Nomes que começam com LC_seguidos por uma letra maiúscula
    • pode ser usado para macros adicionais especificando atributos de localidade.
  • Nomes de todas as funções matemáticas existentes com sufixo fou lsão reservados
    • para funções correspondentes que operam em argumentos flutuantes e longos duplos, respectivamente.
  • Os nomes que começam com SIGseguidos por uma letra maiúscula são reservados
    • para nomes de sinais adicionais.
  • Os nomes que começam com SIG_seguidos por uma letra maiúscula são reservados
    • para ações de sinal adicionais.
  • Nomes que começam com str, memou wcsseguido de uma letra minúscula são reservados
    • para funções adicionais de string e array.
  • Nomes que começam com PRIou são SCNseguidos por qualquer letra minúscula ou Xsão reservados
    • para macros especificadoras de formato adicionais
  • Os nomes que terminam com _tsão reservados
    • para nomes de tipo adicionais.

Embora o uso desses nomes para seus próprios propósitos no momento não possa causar problemas, eles aumentam a possibilidade de conflito com versões futuras desse padrão.


Pessoalmente, simplesmente não inicio identificadores com sublinhados. Nova adição à minha regra: não use sublinhados duplos em nenhum lugar, o que é fácil, pois raramente uso sublinhado.

Depois de pesquisar neste artigo, não termino mais meus identificadores, _t pois isso é reservado pelo padrão POSIX.

A regra sobre qualquer identificador que termina com _tme surpreendeu bastante. Eu acho que esse é um padrão POSIX (ainda não tenho certeza) procurando esclarecimentos, capítulos e versos oficiais. Isto é do manual GNU libtool , listando nomes reservados.

O CesarB forneceu o seguinte link para os símbolos reservados do POSIX 2004 e observa 'que muitos outros prefixos e sufixos reservados ... podem ser encontrados lá'. Os símbolos reservados do POSIX 2008 são definidos aqui. As restrições são um pouco mais sutis do que as acima.

Roger Pate
fonte
14
O padrão C ++ não "importa" o C, não é? Eles importam determinados cabeçalhos, mas não o idioma como um todo, ou regras de nomes, tanto quanto eu sei. Mas sim, o que me surpreendeu também. Mas, como é C, só pode ser aplicado aos ns globais. Deve ser seguro para uso _T classes dentro como eu lê-lo
jalf
27
O padrão C ++ não "importa" o padrão C. Ele faz referência a C padrão. A introdução da biblioteca C ++ diz "A biblioteca também disponibiliza os recursos da biblioteca C padrão". Isso é feito incluindo os cabeçalhos da biblioteca C Standard com as alterações apropriadas, mas não "importando". O padrão C ++ possui um conjunto próprio de regras que descreve os nomes reservados. Se um nome reservado em C deve ser reservado em C ++, esse é o lugar para dizer isso. Mas o padrão C ++ não diz isso. Portanto, não acredito que as coisas reservadas em C sejam reservadas em C ++ - mas eu poderia estar errado.
Johannes Schaub - litb 20/09/09
8
Foi o que descobri sobre o problema "_t": o n1256 (C99 TC3) diz: "Os nomes dos tipos digitados que começam com int ou uint e terminam com _t" estão reservados. Eu acho que ainda permite o uso de nomes como "foo_t" - mas acho que eles são reservados pelo POSIX.
Johannes Schaub - litb 20/09/09
59
Então 'tolerância' é reservada pelo POSIX, pois começa com 'para' + uma letra minúscula? Aposto que muito código quebra essa regra!
Sjoerd
23
@LokiAstari, " O padrão C ++ é definido em termos do padrão C. Basicamente, ele diz que o C ++ é C com essas diferenças e acréscimos. " Bobagem! O C ++ apenas referencia o padrão C em [basic.fundamental] e a biblioteca. Se o que você diz é verdade, onde o C ++ diz isso _Boole _Imaginarynão existe no C ++? A linguagem C ++ é definida explicitamente, não em termos de "edições" em C, caso contrário, o padrão pode ser muito menor!
27613 Jonathan Wakely
198

As regras para evitar a colisão de nomes estão no padrão C ++ (consulte o livro Stroustrup) e são mencionadas pelos gurus do C ++ (Sutter, etc.).

Regra pessoal

Como não queria lidar com casos e queria uma regra simples, projetei uma regra pessoal, simples e correta:

Ao nomear um símbolo, você evitará colisões com as bibliotecas do compilador / OS / padrão se:

  • nunca inicie um símbolo com um sublinhado
  • nunca nomeie um símbolo com dois sublinhados consecutivos no interior.

Obviamente, colocar seu código em um namespace exclusivo também ajuda a evitar colisões (mas não protege contra macros ruins)

Alguns exemplos

(Eu uso macros porque elas são mais poluidoras de código de símbolos C / C ++, mas pode ser qualquer coisa, desde o nome da variável ao nome da classe)

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

Extrai do rascunho C ++ 0x

Do n3242.pdf arquivo (espero que o texto padrão final seja similar):

17.6.3.3.2 Nomes globais [global.names]

Certos conjuntos de nomes e assinaturas de funções são sempre reservados para a implementação:

- Cada nome que contém um sublinhado duplo _ _ ou começa com um sublinhado seguido por uma letra maiúscula (2.12) é reservado à implementação para qualquer uso.

- Cada nome que começa com um sublinhado é reservado à implementação para uso como um nome no espaço para nome global.

Mas também:

17.6.3.3.5 Sufixos literais definidos pelo usuário [usrlit.suffix]

Identificadores de sufixo literal que não começam com um sublinhado são reservados para futura padronização.

Esta última cláusula é confusa, a menos que você considere que um nome começando com um sublinhado e seguido por uma letra minúscula seria OK se não definido no espaço para nome global ...

paercebal
fonte
9
@Meysam: __WRONG_AGAIN__contém dois sublinhados consecutivos (dois no início e dois no final), então isso está errado de acordo com o padrão.
paercebal
8
@ BЈовић: WRONG__WRONGcontém dois sublinhados consecutivos (dois no meio), então isso está errado de acordo com o padrão
paercebal
2
colocar seu código em um namespace exclusivo também ajuda a evitar colisões : mas isso ainda não é suficiente, pois o identificador pode colidir com uma palavra-chave independentemente do escopo (por exemplo, __attribute__para o GCC).
Ruslan
1
Por que existe algum problema de ter dois sublinhados consecutivos no meio, de acordo com o padrão? Os sufixos literais definidos pelo usuário aplicam-se a valores literais como 1234567Lor 4.0f; O IIRC refere-se 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 o padrão diz que esses são reservados. Este não é um conselho sobre estilo bom ou ruim. É uma decisão do padrão. Por que eles decidiram isso? Eu acho que os primeiros compiladores já usavam essas convenções informalmente antes da padronização.
paercebal
38

Do MSDN :

O uso de dois caracteres de sublinhado seqüencial (__) no início de um identificador ou um único sublinhado inicial seguido por uma letra maiúscula é reservado para implementações do C ++ em todos os escopos. Evite usar um sublinhado à esquerda seguido de uma letra minúscula para nomes com escopo de arquivo, devido a possíveis conflitos com identificadores reservados atuais ou futuros.

Isso significa que você pode usar um único sublinhado como prefixo de variável de membro, desde que seja seguido por uma letra minúscula.

Aparentemente, isso foi retirado da seção 17.4.3.1.2 do padrão C ++, mas não consigo encontrar uma fonte original para o padrão completo online.

Veja também esta pergunta .

Roger Lipscombe
fonte
2
Encontrei um texto semelhante no n3092.pdf (o rascunho do padrão C ++ 0x) na seção: "17.6.3.3.2 Nomes globais"
paercebal
7
Curiosamente, esta parece ser a única resposta que tem resposta direta e concisa à pergunta.
hyde
9
@hyde: Na verdade, não é, pois está pulando a regra para não ter nenhum identificador com um sublinhado principal no espaço de nome global. Veja a resposta de Roger . Eu ficaria muito cauteloso com as citações de documentos do MS VC como uma autoridade no padrão C ++.
Sb 9/07
@sbi eu estava me referindo a "você pode usar um único sublinhado como prefixo de variável membro, desde que seja seguido por uma letra minúscula" nesta resposta, que responde a pergunta no texto da pergunta direta e concisa, sem se afogar em uma parede de texto.
Hyde
5
Primeiro, ainda considero a falta de qualquer sugestão de que a mesma regra não se aplique ao espaço para nome global como uma falha. O pior é que os sublinhados adjacentes são proibidos não apenas no início, mas em qualquer lugar , de um identificador. Portanto, essa resposta não está apenas omitindo um fato, mas na verdade faz pelo menos uma afirmação ativamente errada. Como eu disse, referir-me aos documentos da MSVC é algo que eu não faria a menos que a pergunta fosse apenas sobre VC.
Sb
25

Quanto à outra parte da pergunta, é comum colocar o sublinhado no final do nome da variável para não colidir com nada interno.

Eu faço isso mesmo dentro de classes e namespaces, porque só preciso lembrar de uma regra (em comparação com "no final do nome no escopo global e no início do nome em qualquer outro lugar").

Max Lybbert
fonte
2

Sim, sublinhados podem ser usados ​​em qualquer lugar do identificador. Acredito que as regras sejam: qualquer um de az, AZ, _ no primeiro caractere e + 0-9 para os seguintes caracteres.

Os prefixos de sublinhado são comuns no código C - um único sublinhado significa "privado" e os sublinhados duplos geralmente são reservados para uso pelo compilador.

John Millikin
fonte
3
Eles são comuns em bibliotecas. Eles não devem ser comuns no código do usuário.
Martin York
43
As pessoas fazem as bibliotecas escrever em C, você sabe.
John Millikin
7
"Sim, sublinhados podem ser usados ​​em qualquer lugar do identificador." Isso está errado para identificadores globais. Veja a resposta de Roger .
Sb 9/07