💡 Resumo:Tutorial de enum em C++: enums clássicos, enum class do C++11, enums dentro de classes e como a reflexão do C++26 (std::meta) finalmente resolve a conversão de enum para string sem macros nem tabelas de busca.
Última atualização:2026-10-24
Tipos de enumeração (enum) em C++¶
Sobre o autor:¶
Conta oficial do WeChat:

Comportamento tradicional¶
As enumerações têm os seguintes problemas:
- O escopo é ilimitado, o que facilita conflitos de nomenclatura. Por exemplo, o código a seguir não compila:
enum Color
{
RED,
BLUE
};
enum Feeling
{
EXCITED,
BLUE
};
int main()
{
Color a = BLUE; // error
Feeling b = EXCITED;
std::cout << a << ":" << b << std::endl;
return 0;
}
A implementação concreta está em: tradition_color.cpp
Prática clássica¶
Uma forma simples de resolver os conflitos de nomenclatura causados pelo escopo ilimitado é adicionar um prefixo ao nomear as variáveis de enumeração, como no exemplo acima, alterando para COLOR_BLUE e FEELING_BLUE.
Em geral, por consistência, adicionamos o prefixo a todas as constantes. Mas, assim, o código que define as variáveis de enumeração fica prolixo. Em programas C, talvez seja inevitável fazer assim; programadores C++, porém, dificilmente apreciam esse método. A alternativa são os namespaces:
Depois disso, é possível definir uma nova variável de enumeração com Color::Type c = Color::RED;. Se for usado using namespace Color, o prefixo pode até ser omitido, simplificando o código. No entanto, como um namespace pode ser estendido posteriormente com novos conteúdos, o fechamento de escopo que ele oferece não é alto. Em projetos grandes, ainda é possível que pessoas diferentes dêem o mesmo nome de tipo de enumeração a coisas diferentes.
A forma mais "eficaz" é usar uma classe ou estrutura para restringir o escopo: por exemplo, o método para definir novas variáveis é o mesmo do namespace acima; a diferença é que assim não precisamos nos preocupar com a classe sendo modificada em outro lugar. Usa-se uma estrutura, e não uma classe, porque justamente se espera que essas constantes sejam publicamente acessíveis.
A implementação concreta está em: classic_practice.cpp
As enum classes do C++11¶
A abordagem acima resolve o primeiro problema, mas continua impotente diante dos outros dois. Felizmente, o padrão C++11 introduziu as "enum classes" (enum class), que resolvem bem os problemas mencionados.
- O escopo do novo enum deixa de ser global
- Não há conversão implícita para outros tipos
/**
* @brief C++11的枚举类
* 下面等价于enum class Color2:int
*/
enum class Color2
{
RED=2,
YELLOW,
BLUE
};
Color2 c2 = Color2::RED;
cout << static_cast<int>(c2) << endl; //必须转!
- É possível especificar um tipo específico para armazenar o enum
enum class Color3:char; // 前向声明
// 定义
enum class Color3:char
{
RED='r',
BLUE
};
char c3 = static_cast<char>(Color3::RED);
A implementação concreta está em: classic_practice.cpp
Tipos de enumeração dentro de classes¶
Às vezes queremos que certas constantes sejam válidas apenas dentro da classe. Como as constantes de macro definidas por #define são globais e não atendem ao objetivo, pensamos em usar const para modificar membros de dados. De fato, os membros de dados const existem, mas seu significado não é o que esperamos.
Um membro de dados const é constante apenas durante o tempo de vida de um objeto; para a classe inteira, ele é variável, porque a classe pode criar vários objetos, e o valor do membro de dados const pode ser diferente em objetos diferentes.
Não é possível inicializar um membro de dados const na declaração da classe. O uso a seguir está errado, porque, enquanto o objeto da classe não for criado, o compilador não sabe qual é o valor de SIZE. (antes do padrão C++11)
O correto é fazê-lo na lista de inicialização do construtor da classe:
class A
{
A(int size); // 构造函数
const int SIZE ;
};
A::A(int size) : SIZE(size) // 构造函数的定义
{
}
A a(100); // 对象 a 的 SIZE 值为 100
A b(200); // 对象 b 的 SIZE 值为 200
Como estabelecer, então, uma constante fixa em toda a classe?
Não conte com membros de dados const; use constantes de enumeração dentro da classe. Por exemplo:
class Person{
public:
typedef enum {
BOY = 0,
GIRL
}SexType;
};
//访问的时候通过,Person::BOY或者Person::GIRL来进行访问。
As constantes de enumeração não ocupam espaço de armazenamento dos objetos; elas são todas avaliadas em tempo de compilação.
A desvantagem das constantes de enumeração é que seu tipo de dados implícito é o inteiro, com valor máximo limitado e sem capacidade de representar números de ponto flutuante.
De enum para string: a resposta da reflexão do C++26¶
Converter um valor de enum no seu nome é um problema que quase todo projeto C++ acaba encontrando, e até o C++26 a biblioteca padrão não tinha resposta. As alternativas, da mais antiga para a mais recente: (1) X-macros, que abusam do pré-processador para manter a lista de enumeradores num só lugar; (2) tabelas de busca escritas à mão ou especialização de templates, onde cada enum novo significa mais uma tabela para manter; (3) bibliotecas de terceiros como magic_enum, que extraem os nomes com truques de compilador, mas impõem limites de tamanho e tipo subjacente.
A reflexão estática do C++26 (proposta P2996, std::meta) finalmente transforma isso em poucas linhas de código de verdade. Uma função enumera os membros de qualquer enum em tempo de compilação e lê o nome de cada enumerador:
#include <meta>
template <typename E>
requires std::is_enum_v<E>
std::string enum_to_string(E value) {
template for (constexpr auto e : std::meta::enumerators_of(^^E)) {
if (value == [:e:]) {
return std::meta::identifier_of(e);
}
}
throw std::invalid_argument("unknown enumerator");
}
Uma única função passa a cobrir todos os enums do código. Renomear um enumerador atualiza a string automaticamente, e uma divergência aparece na compilação em vez de virar um rótulo errado em runtime.
Um aviso honesto: o trecho segue a P2996 conforme entrou no draft do C++26. As grafias exatas ainda estavam se assentando nas previews dos compiladores (EDG e Clang explorers), então confira a tabela de suporte da sua toolchain antes de usar. Até lá, a tabela de busca continua sendo a resposta mais portátil e sem dependências.
Perguntas frequentes¶
Como converto um enum para string em C++?¶
Antes do C++26 não existe forma padrão: escolha entre tabela de busca, especialização de templates, X-macros ou uma biblioteca como magic_enum. A reflexão estática do C++26 enumera os membros em compilação e lê o nome diretamente, eliminando a manutenção das quatro.
A biblioteca padrão tem conversão embutida de enum para string?¶
Só no C++26. Streams e std::to_string formatam apenas o inteiro subjacente, e é por isso que todo projeto acaba escrevendo o próprio helper.
Quando vou poder usar a reflexão do C++26?¶
O C++26 é o próximo padrão e a reflexão já está no draft; EDG e os explorers do Clang já compilam os exemplos. Para produção, acompanhe o suporte do compilador e mantenha uma tabela de busca como fallback.
Tópicos relacionados¶
- Funções virtuais puras e classes abstratas em C++ | C++那些事
- Asserções com assert em C++ | C++那些事
- Bit fields em C++ | C++那些事
- Implementando polimorfismo de C++ em linguagem C | C++那些事
- const em C++ explicado: constantes, ponteiro const vs ponteiro para const | C++那些事
- A família completa do const em C++ | C++那些事
- O operador de desreferência * em C++ | C++那些事
- Inicialização e atribuição em C++ | C++那些事
- Formas de retornar múltiplos valores em C++ | C++那些事
- A palavra-chave mutable em C++ | C++那些事