Modelo (C ++) - Template (C++)
Os modelos são um recurso da linguagem de programação C ++ que permite que funções e classes operem com tipos genéricos . Isso permite que uma função ou classe trabalhe em muitos tipos de dados diferentes sem ser reescrita para cada um.
A C ++ Standard Library fornece muitas funções úteis dentro de uma estrutura de modelos conectados.
As principais inspirações para os modelos C ++ foram os módulos parametrizados fornecidos pelo CLU e os genéricos fornecidos pela Ada .
Visão geral técnica
Existem três tipos de modelos: modelos de funções , modelos de classes e, desde C ++ 14 , modelos de variáveis . Desde C ++ 11 , os modelos podem ser variáveis ou não variáveis; nas versões anteriores do C ++, eles são sempre não variáveis.
Modelos de função
Um template de função se comporta como uma função, exceto que o template pode ter argumentos de muitos tipos diferentes (veja o exemplo). Em outras palavras, um template de função representa uma família de funções. O formato para declarar modelos de função com parâmetros de tipo é:
template<class identifier> declaration;
template<typename identifier> declaration;
Ambas as expressões têm o mesmo significado e se comportam exatamente da mesma maneira. A última forma foi introduzida para evitar confusão, uma vez que um parâmetro de tipo não precisa ser uma classe até C ++ 20. (Pode ser um tipo básico, como int
ou double
.)
Por exemplo, a Biblioteca Padrão C ++ contém o modelo de função max(x, y)
que retorna o maior de x
e y
. Esse modelo de função pode ser definido assim:
template<typename T> T max(T a, T b) { return a > b ? a : b; }
Esta definição de função única funciona com muitos tipos de dados. Especificamente, ele funciona com todos os tipos de dados para os quais > (o operador maior que) é definido. O uso de um modelo de função economiza espaço no arquivo de código-fonte, além de limitar as alterações a uma descrição de função e tornar o código mais fácil de ler.
Um modelo não produz código de objeto menor, entretanto, em comparação com a escrita de funções separadas para todos os diferentes tipos de dados usados em um programa específico. Por exemplo, se um programa usa uma int
e uma double
versão do max()
modelo de função mostrado acima, o compilador criará uma versão de código de objeto max()
que opera em int
argumentos e outra versão de código de objeto que opera em double
argumentos. A saída do compilador será idêntica à que teria sido produzida se o código-fonte contivesse duas versões não padronizadas de max()
, uma escrita para manipular int
e outra escrita para manipular double
.
Aqui está como o template de função pode ser usado:
#include <iostream>
int main() {
// This will call max<int> by implicit argument deduction.
std::cout << max(3, 7) << '\n';
// This will call max<double> by implicit argument deduction.
std::cout << max(3.0, 7.0) << '\n';
// We need to explicitly specify the type of the arguments;
// although std::type_identity could solve this problem...
std::cout << max<double>(3, 7.0) << '\n';
}
Nos primeiros dois casos, o argumento do modelo T
é deduzido automaticamente pelo compilador como sendo int
e double
, respectivamente. No terceiro caso, a dedução automática de max(3, 7.0)
falhará porque o tipo dos parâmetros deve, em geral, corresponder exatamente aos argumentos do modelo. Portanto, instanciamos explicitamente a double
versão com max<double>()
.
Este modelo de função pode ser instanciado com qualquer tipo construtível por cópia para o qual a expressão y > x
é válida. Para tipos definidos pelo usuário, isso implica que o operador maior que ( >
) deve ser sobrecarregado no tipo.
Modelos de aulas
Um modelo de classe fornece uma especificação para gerar classes com base em parâmetros. Os modelos de classe geralmente são usados para implementar contêineres . Um modelo de classe é instanciado passando um determinado conjunto de tipos para ele como argumentos de modelo. A C ++ Standard Library contém muitos modelos de classes, em particular os containers adaptados da Standard Template Library , como vector
.
Modelos de variáveis
No C ++ 14, os modelos também podem ser usados para variáveis, como no exemplo a seguir:
template<typename T>
constexpr T pi = T{3.141592653589793238462643383L}; // (Almost) from std::numbers::pi
Especialização de modelo
Quando uma função ou classe é instanciada a partir de um modelo, uma especialização desse modelo é criada pelo compilador para o conjunto de argumentos usados e a especialização é referida como sendo uma especialização gerada.
Especialização de modelo explícito
Às vezes, o programador pode decidir implementar uma versão especial de uma função (ou classe) para um determinado conjunto de argumentos de tipo de modelo que é chamado de especialização explícita. Desta forma, certos tipos de modelo podem ter uma implementação especializada que é otimizada para o tipo ou uma implementação mais significativa do que a implementação genérica.
- Se um modelo de classe é especializado por um subconjunto de seus parâmetros, ele é chamado de especialização parcial do modelo (os modelos de função não podem ser parcialmente especializados).
- Se todos os parâmetros forem especializados, trata-se de uma especialização completa .
A especialização explícita é usada quando o comportamento de uma função ou classe para escolhas específicas dos parâmetros do modelo deve se desviar do comportamento genérico: isto é, do código gerado pelo modelo principal, ou modelos. Por exemplo, a definição do modelo abaixo define uma implementação específica de max()
para argumentos do tipo const char*
:
#include <cstring>
template<>
const char* max(const char* a, const char* b) {
// Normally, the result of a direct comparison
// between two C strings is undefined behaviour;
// using std::strcmp makes defined.
return std::strcmp(a, b) > 0 ? a : b;
}
Modelos variados
C ++ 11 introduziu modelos variadic , que podem assumir um número variável de argumentos de uma maneira um pouco semelhante a funções variadic como std::printf
.
Aliases de modelo
C ++ 11 introduziu aliases de template, que agem como typedefs parametrizados .
O código a seguir mostra a definição de um alias de modelo StrMap
. Isso permite, por exemplo, StrMap<int>
ser usado como uma abreviação para std::unordered_map<int,std::string>
.
template<typename T> using StrMap = std::unordered_map<T, std::string>;
Recursos de programação genéricos em outras linguagens
Inicialmente, o conceito de templates não foi incluído em algumas linguagens, como Java e C # 1.0. A adoção do Java de genéricos imita o comportamento dos modelos, mas é tecnicamente diferente. C # adicionou genéricos (tipos parametrizados) no .NET 2.0. Os genéricos em Ada são anteriores aos modelos C ++.
Embora os modelos C ++, os genéricos Java e os genéricos .NET sejam frequentemente considerados semelhantes, os genéricos apenas imitam o comportamento básico dos modelos C ++. Alguns dos recursos avançados de modelo utilizados por bibliotecas, como Boost e STLSoft , e implementações do próprio STL, para metaprogramação de modelo (especialização explícita ou parcial, argumentos de modelo padrão, argumentos de modelo não-tipo, argumentos de modelo de modelo, ...) são não disponível com genéricos.
Nos modelos C ++, os casos de tempo de compilação eram historicamente executados por correspondência de padrões sobre os argumentos do modelo. Por exemplo, a classe base do modelo no exemplo de fatorial abaixo é implementada combinando 0 em vez de com um teste de desigualdade, que não estava disponível anteriormente. No entanto, a chegada ao C ++ 11 de recursos de biblioteca padrão, como std :: condicional, forneceu outra maneira mais flexível de lidar com a instanciação de modelo condicional.
// Induction
template<unsigned N>
struct Factorial {
static constexpr unsigned value = N * Factorial<N - 1>::value;
};
// Base case via template specialization:
template<> struct Factorial<0> {
static constexpr unsigned value = 1;
};
Com essas definições, pode-se calcular, digamos, 6! em tempo de compilação usando a expressão Factorial<6>::value
. Alternativamente, constexpr
em C ++ 11 / consteval
em C ++ 20 pode ser usado para calcular tais valores diretamente usando uma função em tempo de compilação. Por causa disso, a metaprogramação de template agora é mais usada para fazer operações em tipos.
Veja também
- Metaprogramação de template
- Metaprogramação
- Monomorfização
- Programação genérica
- A falha de substituição não é um erro
- Padrão de template curiosamente recorrente
- Lista de bibliotecas de modelos C ++
Referências
links externos
- Demonstração da completude de Turing de modelos C ++ (implementação de cálculo Lambda)