typedef vs using em C++: guia completo para o desenvolvedor moderno
\n\nVocê já se perguntou por que em código C++ alguns desenvolvedores escrevem typedef e outros — using? Parece que ambos fazem a mesma coisa — criam aliases para tipos. Mas será que é realmente assim? Se você quer escrever código limpo e moderno e não cair em armadilhas de construções obsoletas, este artigo é para você.
Vamos analisar como typedef e using diferem em C++, quando usar cada um e por que a comunidade C++ está cada vez mais inclinada ao using. Pronto? Vamos lá!
Breve história: do C ao C++11
\n\ntypedef veio da linguagem C. É a forma clássica de declarar um alias para um tipo. Fica assim:
\n\ntypedef unsigned long long ULL;\ntypedef std::vector<std::string> StringVector;\ntypedef void (*FunctionPtr)(int, double);\n\nA sintaxe, para dizer o mínimo, não é a mais intuitiva. Isso é especialmente perceptível com ponteiros para funções — até desenvolvedores experientes às vezes hesitam ao ler tais declarações.
\n\nNo C++11, surgiu uma nova construção — using (ou declaração de alias). Ela parece muito mais natural:
\n\nusing ULL = unsigned long long;\nusing StringVector = std::vector<std::string>;\nusing FunctionPtr = void (*)(int, double);\n\nImediatamente fica claro: à esquerda — o nome, à direita — o tipo. Lê-se como uma "atribuição", o que é intuitivo para qualquer programador.
\n\nPrincipal diferença: trabalho com templates
\n\nAqui está a diferença mais importante entre typedef e using. using suporta alias templates (aliases de template), enquanto typedef não.
Vamos ver um exemplo. Suponha que você queira criar um alias para std::unique_ptr com um deletador personalizado:
// Com using — fácil e elegante\ntemplate <typename T>\nusing CustomPtr = std::unique_ptr<T, void(*)(T*)>;\n\n// Com typedef — impossível diretamente!\n// É preciso criar wrappers através de structs\ntemplate <typename T>\nstruct CustomPtrOld {\n typedef std::unique_ptr<T, void(*)(T*)> type;\n};\n\n// Uso:\nCustomPtr<int> ptr1; // using — simples\nCustomPtrOld<int>::type ptr2; // typedef — complicado\n\nPercebe a diferença? using torna o código mais limpo e curto. E se você precisar usar esse alias dentro de uma classe ou função template, a diferença fica ainda mais evidente:
template <typename T>\nclass Container {\npublic:\n // Com using\n using Ptr = CustomPtr<T>;\n \n // Com typedef — precisa de typename e ::type\n typedef typename CustomPtrOld<T>::type OldPtr;\n \nprivate:\n Ptr data;\n OldPtr oldData;\n};\n\nLegibilidade do código: vencedor óbvio
\n\nMesmo para tipos simples, using ganha em legibilidade. Compare:
// Com typedef — não está claro onde está o nome e onde está o tipo\ntypedef std::map<std::string, std::vector<int>> StringToIntVectorMap;\n\n// Com using — tudo é óbvio\nusing StringToIntVectorMap = std::map<std::string, std::vector<int>>;\n\nAo ler o código, seu olho naturalmente escaneia a construção da esquerda para a direita. using segue esse princípio: primeiro o nome, depois o tipo. typedef força você a ler primeiro o tipo e depois o nome — isso contradiz a intuição.
Ponteiros para funções: o grande problema do typedef
\n\nPonteiros para funções são, talvez, a parte menos amada do C++ para muitos desenvolvedores. E typedef os torna ainda mais confusos:
// typedef — leia como um feitiço\ntypedef void (*Callback)(int, const std::string&);\n\n// using — como uma variável comum\nusing Callback = void (*)(int, const std