Sublinhados em C++: regras não óbvias que quebram o código

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Sublinhados em C++: assassinos silenciosos do seu código

\n\n

Imagine a situação: você está escrevendo um código C++ limpo e bonito. O compilador fica em silêncio, o linter não reclama, tudo funciona perfeitamente. E então — bum! — e em algum outro compilador ou com uma nova versão do padrão, tudo quebra sem motivo aparente. Familiar?

\n\n

Na maioria das vezes, a raiz do mal está nos caracteres de sublinhado (_) aparentemente inofensivos. Este tópico é um verdadeiro "cemitério" para iniciantes e até mesmo para desenvolvedores C++ experientes. Não é à toa que a pergunta no Stack Overflow sobre as regras de uso de sublinhados acumulou dezenas de milhares de visualizações. Vamos entender de uma vez por todas o que pode, o que não pode e como não cair na armadilha do comportamento indefinido (UB).

\n\n

Por que o padrão C++ é tão rigoroso com sublinhados?

\n\n

A raiz do problema está na compatibilidade e no espaço de nomes global. A biblioteca padrão do C++ usa ativamente sublinhados para suas necessidades internas. Se você acidentalmente criar um identificador que entre em conflito com nomes internos da biblioteca ou do compilador — pronto, é o fim. O compilador pode redefinir silenciosamente sua variável, e você terá um comportamento indefinido (Undefined Behavior), que se manifestará no momento mais inoportuno.

\n\n

O padrão C++ (a partir do C++03 e em todas as versões subsequentes, incluindo C++20 e C++23) especifica claramente quais identificadores são reservados para a implementação. Violar essas regras torna seu código não apenas incorreto, mas não portável.

\n\n

Top 5 erros fatais com sublinhados

\n\n

Vamos analisar os erros mais comuns em que até programadores experientes pisam.

\n\n

Erro nº1: Sublinhado duplo em qualquer lugar

\n\n

Regra: Qualquer identificador que contenha dois caracteres de sublinhado consecutivos (__) em qualquer lugar é reservado para a implementação.

\n\n
// RUIM! Reservado para a "implementação"\nint __myVariable = 42;\nvoid my__function();\nclass My__Class {};\n\n// BOM\nint myVariable = 42;\nvoid my_function();\nclass MyClass {};\n
\n\n

Por que isso é perigoso? O compilador pode usar __myVariable para seus macros internos. Por exemplo, muitos compiladores definem __LINE__ ou __cplusplus. Se você criar seu próprio __LINE__, o compilador pode simplesmente ignorar sua definição ou, pior ainda, substituí-la pelo seu próprio valor. O resultado é UB.

\n\n

Erro nº2: Sublinhado + letra maiúscula no escopo global

\n\n

Regra: Um identificador que começa com sublinhado, seguido por uma letra maiúscula (A-Z), é reservado para a implementação em qualquer escopo.

\n\n
// RUIM! Mesmo em escopo local!\nvoid foo() {\n    int _MyVar = 10; // Violação da regra\n}\n\n// RUIM! Globalmente — ainda mais\nint _GlobalVar = 20;\n\n// BOM\nvoid foo() {\n    int myVar = 10;\n}\n
\n\n

Esta regra é a mais traiçoeira. Muitos pensam: "Bem, estou dentro de uma função, quem isso incomoda?". Incomoda! O padrão não abre exceções para escopo local se o identificador começar com sublinhado e letra maiúscula. O compilador tem todo o direito de reservar esses nomes para seus próprios fins.

\n\n

Erro nº3: Sublinhado no início + letra minúscula no escopo global

\n\n

Regra: Um identificador que começa com sublinhado, seguido por uma letra minúscula (a-z), é reservado para a implementação no escopo global (namespace scope).

\n\n
// RUIM! No espaço de nomes global\nint _myGlobal = 0;\nvoid _helperFunction();\n\n// BOM — dentro de uma função ou classe\nclass MyClass {\n    int _memberVar; // Dentro da classe — pode (mas é melhor m_)\n};\n\nvoid foo() {\n    int _localVar = 5; // Dentro da fun                

Blogs

Book Recommendations