Padrões C++ em .cpp: como não duplicar código e manter a sanidade

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

Templates C++: por que seu código não linka (e como corrigir isso)

\n

Você já escreveu uma função template elegante, colocou cuidadosamente a declaração no .h, a definição no .cpp, compilou o projeto... e recebeu o erro ameaçador do linker? undefined reference — um abraço de 1998. Familiar? Se sim, você está no lugar certo. Se não, ou você é um gênio, ou nunca tentou separar templates. Bem-vindo ao clube.

\n

Neste artigo, vamos analisar a pergunta mais popular do Stack Overflow (sim, aquela com milhares de visualizações): «Storing C++ template function definitions in a .CPP file». Não vamos apenas copiar a resposta — vamos entender por que funciona dessa forma e aprender a aplicar na prática. Vamos lá!

\n\n

O problema: templates não são funções comuns

\n

Imagine que você está escrevendo uma biblioteca em C++ (ou apenas um projeto educacional para PythonLib). Você tem uma função template max:

\n
// max.h\ntemplate <typename T>\nT max(T a, T b);
\n
// max.cpp\ntemplate <typename T>\nT max(T a, T b) {\n    return (a > b) ? a : b;\n}
\n
// main.cpp\n#include "max.h"\nint main() {\n    int result = max(3, 7);  // Erro do linker!\n    return 0;\n}
\n

O compilador de main.cpp vê apenas a declaração no .h. Ele gera a chamada da função max<int>. Mas onde está o corpo? Em max.cpp — mas o compilador não sabe que int será necessário! Ele compila max.cpp separadamente e, não vendo nenhuma chamada a max<int>, simplesmente não cria essa especialização. O resultado é um erro na fase de linkagem.

\n

Ideia chave: um template não é código, é um molde para código. Até que o compilador veja um tipo concreto, ele não gera código de máquina. Portanto, a separação clássica em .h e .cpp para templates não funciona de imediato.

\n\n

Solução #1: Tudo no arquivo de cabeçalho (Header-Only)

\n

A abordagem mais simples e frequentemente usada é colocar tanto a declaração quanto a definição no arquivo .h. Sim, isso aumenta o tempo de compilação (cada .cpp que inclui esse header irá instanciar o template novamente), mas não há problemas com linkagem.

\n
// max.h (header-only)\n#ifndef MAX_H\n#define MAX_H\n\ntemplate <typename T>\nT max(T a, T b) {\n    return (a > b) ? a : b;\n}\n\n#endif
\n

Prós: simplicidade, sem erros de linkagem, ideal para projetos pequenos ou bibliotecas onde templates são usados raramente.

\n

Contras: inchaço do código na compilação, aumento do tempo de build, impossibilidade de ocultar a implementação (se você distribuir a biblioteca como binário compilado).

\n\n

Solução #2: Instanciação explícita (Explicit Instantiation)

\n

E aqui está o herói do nosso artigo. Se você sabe de antemão com quais tipos o template será usado, pode pedir explicitamente ao compilador para criar essas especializações no arquivo .cpp. Esta é a resposta para a pergunta «Storing C++ template function definitions in a .CPP file».

\n
// max.h\n#ifndef MAX_H\n#define MAX_H\n\ntemplate <typename T>\nT max(T a, T b);\n\n// Informamos ao linker que as especializações estarão em outro lugar\nextern template int max(int, int);\nextern template double max(double, double);\n\n#endif
\n
// max.cpp\n#include "max.h"\n\ntemplate <typename T>\nT max(T a, T b) {\n    return (a > b) ? a : b;\n}\n\n// Instanciamos explicitamente os tipos necessários\ntemplate int max(int, int);\ntemplate double max(double, double);
\n
// main.cpp\n#include "max.h"\nint main() {\n    int a = max(3, 7);      // OK\n    double b = max(3.14, 2.71); // OK\n    // float c = max(1.0f, 2.0f); // Erro                

Blogs

Book Recommendations