Especialização de Templates
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 66 de 104.
Às vezes, um modelo genérico não funciona bem para todos os tipos. Por exemplo, comparar strings no estilo C com > compara endereços de ponteiros, não o texto em si. A especialização de modelo permite fornecer uma implementação personalizada para tipos específicos, mantendo a versão genérica para todos os outros.
Uma especialização completa substitui o template inteiro para um tipo específico. Você a declara com uma lista de parâmetros de template vazia e especifica o tipo concreto:
// Template primário
template <typename T>
class Printer {
public:
void print(T value) {
std::cout << value << std::endl;
}
};
// Especialização completa para const char*
template <>
class Printer<const char*> {
public:
void print(const char* value) {
std::cout << "String: " << value << std::endl;
}
};
Printer<int> intPrinter;
intPrinter.print(42); // Saída: 42
Printer<const char*> strPrinter;
strPrinter.print("hello"); // Saída: String: helloOs modelos de função também podem ser especializados:
template <typename T>
bool isEqual(T a, T b) {
return a == b;
}
template <>
bool isEqual<const char*>(const char* a, const char* b) {
return std::strcmp(a, b) == 0;
}
isEqual(5, 5); // Usa a versão genérica
isEqual("hi", "hi"); // Usa a versão especializadaO compilador sempre prefere a correspondência mais específica — se existir uma especialização para o tipo exato que está sendo usado, ela será escolhida em vez do template genérico.
Desafio
FácilVamos criar um sistema de formatação ciente de tipos que demonstra como a especialização de templates permite personalizar o comportamento para tipos específicos, mantendo uma alternativa genérica para todos os demais.
Você criará dois arquivos para organizar seu código de templates:
Formatter.h: Defina um template de classe chamadoFormatterque formata valores para exibição. Seu template genérico deve funcionar com qualquer tipo, mas você também criará versões especializadas para tipos que precisam de tratamento personalizado.O template principal
Formatterdeve ter:- Um método
format()que recebe um valor do tipo T e imprime:Value: <value>
Crie uma especialização completa para
boolque imprima texto legível em vez de 1 ou 0:- O método
format()deve imprimir:Boolean: trueouBoolean: false
Crie uma especialização completa para
const char*que adicione aspas ao redor das strings:- O método
format()deve imprimir:String: "<value>"
Crie também um template de função chamado
formatPairque receba dois valores do mesmo tipo e os imprima juntos como:Pair: [<first>, <second>]Crie uma especialização de
formatPairparaconst char*que envolva cada string em aspas:Pair: ["<first>", "<second>"]- Um método
main.cpp: Leia quatro entradas (cada uma em uma linha separada):- Um inteiro
- Um double
- Um booleano como string (
trueoufalse) - Um valor de string
Demonstre seus formatadores criando objetos
Formatterapropriados e chamandoformat()para cada tipo:- Formate o inteiro usando
Formatter<int> - Formate o double usando
Formatter<double> - Formate o booleano usando
Formatter<bool>(converta primeiro a entrada de string em um bool) - Formate a string usando
Formatter<const char*>
Em seguida, demonstre a especialização do template de função:
- Chame
formatPaircom dois inteiros: 10 e 20 - Chame
formatPaircom duas strings C:"hello"e"world"
Por exemplo, com as entradas 42, 3.14, true e Hello:
Value: 42
Value: 3.14
Boolean: true
String: "Hello"
Pair: [10, 20]
Pair: ["hello", "world"]Observe como o template genérico trata inteiros e doubles de forma idêntica, enquanto as versões especializadas para bool e const char* fornecem uma formatação personalizada. O compilador seleciona automaticamente a correspondência mais específica para cada tipo usado.
Experimente você mesmo
#include <iostream>
#include <string>
#include "Formatter.h"
using namespace std;
int main() {
// Leia as entradas
int intVal;
double doubleVal;
string boolStr;
string strVal;
cin >> intVal;
cin >> doubleVal;
cin >> boolStr;
cin >> strVal;
// Converta string para bool
bool boolVal = (boolStr == "true");
// TODO: Crie Formatter<int> e formate o inteiro
// TODO: Crie Formatter<double> e formate o double
// TODO: Crie Formatter<bool> e formate o booleano
// TODO: Crie Formatter<const char*> e formate a string
// Dica: Use strVal.c_str() para obter const char*
// TODO: Chame formatPair com dois inteiros: 10 e 20
// TODO: Chame formatPair com duas C-strings: "hello" e "world"
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner GenéricoPratique por conta própria: Compilador de C++ online