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 intou double.)

Por exemplo, a Biblioteca Padrão C ++ contém o modelo de função max(x, y)que retorna o maior de xe 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 inte uma doubleversão do max()modelo de função mostrado acima, o compilador criará uma versão de código de objeto max()que opera em intargumentos e outra versão de código de objeto que opera em doubleargumentos. 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 inte 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 inte 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 doubleversã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, constexprem C ++ 11 / constevalem 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

Referências

links externos