Article de reference

Modèle (C++)

( Learn how and when to remove this message ) Les modèles sont une fonctionnalité du langage de programmation C++ qui permet aux fonctions et aux classes de fonctionner avec des...

(Learn how and when to remove this message)

Les modèles sont une fonctionnalité du langage de programmation C++ qui permet aux fonctions et aux classes de fonctionner avec des types génériques . Cela permet à une déclaration de fonction ou de classe de référencer via une variable générique une autre classe différente ( type de données intégré ou nouvellement déclaré ) sans créer de déclaration complète pour chacune de ces différentes classes.

En termes simples, une classe ou une fonction basée sur un modèle serait l'équivalent (avant la « compilation ») de copier et coller le bloc de code basé sur un modèle dans lequel il est utilisé, puis de remplacer le paramètre de modèle par le paramètre réel. Pour cette raison, les classes utilisant des méthodes basées sur un modèle placent l'implémentation dans les en-têtes (fichiers *.h) car aucun symbole ne pourrait être compilé sans connaître le type au préalable.

La bibliothèque standard C++ fournit de nombreuses fonctions utiles dans un cadre de modèles connectés.

Les principales inspirations pour les modèles C++ ont été les modules paramétrés fournis par le langage CLU et les génériques fournis par Ada .

Aperçu technique

Il existe trois types de modèles : les modèles de fonctions , les modèles de classes et, depuis C++14 , les modèles de variables . Depuis C++11 , les modèles peuvent être variadiques ou non variadiques ; dans les versions antérieures de C++, ils étaient toujours non variadiques.

Modèles de fonctions

Un modèle de fonction se comporte comme une fonction, à ceci près qu'il peut avoir des arguments de plusieurs types différents (voir l'exemple). En d'autres termes, un modèle de fonction représente une famille de fonctions. Le format de déclaration des modèles de fonction avec des paramètres de type est le suivant :

modèle < identifiant de classe > déclaration ; modèle < identifiant de type > déclaration ;

Les deux expressions ont la même signification et se comportent exactement de la même manière. La dernière forme a été introduite pour éviter toute confusion, car un paramètre de type n'a pas besoin d'être une classe avant C++20. (Il peut s'agir d'un type de base tel que intou double.)

Par exemple, la bibliothèque standard C++ contient la fonction template max(x, y)qui renvoie la valeur la plus élevée entre xet y. Cette fonction template pourrait être définie comme ceci :

modèle < typename T > const T & max ( const T & a , const T & b ) { return a < b ? b : a ; }

Cette définition de fonction unique fonctionne avec de nombreux types de données. Plus précisément, elle fonctionne avec tous les types de données pour lesquels < (l'opérateur inférieur à) est défini et renvoie une valeur avec un type convertible en bool. L'utilisation d'un modèle de fonction permet d'économiser de l'espace dans le fichier de code source en plus de limiter les modifications à une description de fonction et de rendre le code plus facile à lire.

Un modèle de fonction instancié produit généralement le même code objet, par rapport à l'écriture de fonctions distinctes pour tous les différents types de données utilisés dans un programme spécifique. Par exemple, si un programme utilise à la fois une version intet une doubleversion du max()modèle de fonction ci-dessus, le compilateur créera une version de code objet de max()qui fonctionne sur intles arguments et une autre version de code objet qui fonctionne sur doubleles arguments. La sortie du compilateur sera identique à ce qui aurait été produit si le code source avait contenu deux versions distinctes non basées sur un modèle de max(), une écrite pour handle intet une écrite pour handle double.

Voici comment le modèle de fonction pourrait être utilisé :

#include <iostream>
int main () { // Ceci appellera max<int> par déduction implicite d'argument. std :: cout << std :: max ( 3 , 7 ) << ' ' ;
// Ceci appellera max<double> par déduction d'argument implicite. 
std :: cout << std :: max ( 3.0 , 7.0 ) << ' ' ;
// Nous devons spécifier explicitement le type des arguments ; 
// bien que std::type_identity puisse résoudre ce problème... std :: cout << max < double > ( 3 , 7.0 ) << ' ' ; }

Dans les deux premiers cas, l'argument du modèle Test automatiquement déduit par le compilateur comme étant intet double, respectivement. Dans le troisième cas, la déduction automatique de max(3, 7.0)échouerait car le type des paramètres doit en général correspondre exactement aux arguments du modèle. Par conséquent, nous instancions explicitement la doubleversion avec max<double>().

Ce modèle de fonction peut être instancié avec n'importe quel type constructible par copie pour lequel l'expression y < xest valide. Pour les types définis par l'utilisateur, cela implique que l'opérateur inférieur à ( <) doit être surchargé dans le type.

Modèles de fonctions abrégées

Depuis C++20 , en utilisant autoou Concept autodans l'un des paramètres d'une déclaration de fonction , cette déclaration devient une déclaration de modèle de fonction abrégée . Une telle déclaration déclare un modèle de fonction et un paramètre de modèle inventé pour chaque espace réservé est ajouté à la liste des paramètres de modèle :

void f1 ( auto ); // identique à template<class T> void f1(T) void f2 ( C1 auto ); // identique à template<C1 T> void f2(T), si C1 est un concept void f3 ( C2 auto ...); // identique à template<C2... Ts> void f3(Ts...), si C2 est un concept void f4 ( C2 auto , ...); // identique à template<C2 T> void f4(T...), si C2 est un concept void f5 ( const C3 auto * , C4 auto & ); // identique à template<C3 T, C4 U> void f5(const T*, U&);

Modèles de cours

Un modèle de classe fournit une spécification pour générer des classes en fonction de paramètres. Les modèles de classe sont généralement utilisés pour implémenter des conteneurs . Un modèle de classe est instancié en lui passant un ensemble donné de types en tant qu'arguments de modèle. La bibliothèque standard C++ contient de nombreux modèles de classe, en particulier les conteneurs adaptés de la bibliothèque de modèles standard , tels que vector.

Modèles variables

En C++14, les modèles peuvent également être utilisés pour les variables, comme dans l'exemple suivant :

modèle < typename T > constexpr T pi = T { 3.141592653589793238462643383L }; // (Presque) à partir de std::numbers::pi

Paramètres de modèle non typés

Bien que le modèle basé sur les types, comme dans les exemples ci-dessus, soit la forme la plus courante de modèle en C++, il est également possible de créer un modèle basé sur des valeurs. Ainsi, par exemple, une classe déclarée avec

modèle < int K > classe MyClass ;

peut être instancié avec un int.

À titre d'exemple concret, le type de tableau de taille fixe de la bibliothèque standard est basé à la fois sur un type (représentant le type d'objet que contient le tableau) et sur un nombre qui est de type (représentant le nombre d'éléments que contient le tableau). peut être déclaré comme suit : std::arraystd::size_tstd::array

modèle < classe T , taille_t N > struct tableau ;

et un tableau de six chars pourrait être déclaré :

tableau < char , 6 > monTableau ;

Spécialisation des modèles

Lorsqu'une fonction ou une classe est instanciée à partir d'un modèle, une spécialisation de ce modèle est créée par le compilateur pour l'ensemble des arguments utilisés, et la spécialisation est appelée spécialisation générée.

Spécialisation explicite des modèles

Parfois, le programmeur peut décider d'implémenter une version spéciale d'une fonction (ou d'une classe) pour un ensemble donné d'arguments de type de modèle, ce que l'on appelle une spécialisation explicite. De cette façon, certains types de modèles peuvent avoir une implémentation spécialisée optimisée pour le type ou une implémentation plus significative que l'implémentation générique.

  • Si un modèle de classe est spécialisé par un sous-ensemble de ses paramètres, on parle de spécialisation de modèle partielle (les modèles de fonction ne peuvent pas être partiellement spécialisés).
  • Si tous les paramètres sont spécialisés, il s'agit d'une spécialisation complète .

La spécialisation explicite est utilisée lorsque le comportement d'une fonction ou d'une classe pour des choix particuliers des paramètres du modèle doit s'écarter du comportement générique : c'est-à-dire du code généré par le ou les modèles principaux. Par exemple, la définition de modèle ci-dessous définit une implémentation spécifique de max()for arguments de type const char*:

#include <chaîne de caractères>
template <> const char * max ( const char * a , const char * b ) { // Normalement, le résultat d'une comparaison directe // entre deux chaînes C est un comportement indéfini ; // l'utilisation de std::strcmp rend défini. return std :: strcmp ( a , b ) > 0 ? a : b ; }

Modèles variadiques

C++11 a introduit les modèles variadiques , qui peuvent prendre un nombre variable d'arguments d'une manière quelque peu similaire aux fonctions variadiques telles que std::printf.

Alias ​​de modèle

C++11 a introduit les alias de modèles, qui agissent comme des typedefs paramétrés .

Le code suivant montre la définition d'un alias de modèle StrMap. Cela permet, par exemple, StrMap<int>de l'utiliser comme raccourci pour std::unordered_map<int,std::string>.

modèle < typename T > utilisant StrMap = std :: unordered_map < T , std :: string > ;

Fonctionnalités de programmation génériques dans d'autres langages

Au départ, le concept de modèles n'était pas inclus dans certains langages, tels que Java et C# 1.0. L'adoption des génériques par Java imite le comportement des modèles, mais est techniquement différente. C# a ajouté des génériques (types paramétrés) dans .NET 2.0. Les génériques dans Ada sont antérieurs aux modèles C++.

Bien que les modèles C++, les génériques Java et les génériques .NET soient souvent considérés comme similaires, les génériques imitent uniquement le comportement de base des modèles C++ . Certaines des fonctionnalités avancées des modèles utilisées par des bibliothèques telles que Boost et STLSoft, ainsi que les implémentations de la STL, pour la métaprogrammation des modèles (spécialisation explicite ou partielle, arguments de modèle par défaut, arguments de modèle non typés, arguments de modèle de modèle, ...) ne sont pas disponibles avec les génériques.

Dans les modèles C++, les cas de compilation étaient historiquement exécutés par correspondance de motifs sur les arguments du modèle. Par exemple, la classe de base du modèle dans l'exemple Factorial ci-dessous est implémentée en faisant correspondre 0 plutôt qu'avec un test d'inégalité, qui n'était pas disponible auparavant. Cependant, l'arrivée dans C++11 de fonctionnalités de bibliothèque standard telles que std::conditional a fourni un autre moyen plus flexible de gérer l'instanciation conditionnelle du modèle.

// 
Modèle d'induction < unsigned N > struct Factorial { static constexpr unsigned value = N * Factorial < N - 1 >:: value ; };
// Cas de base via la spécialisation de modèle : 
template <> struct Factorial < 0 > { static constexpr unsigned value = 1 ; };

Avec ces définitions, on peut calculer, disons, 6! au moment de la compilation en utilisant l'expression Factorial<6>::value.

Alternativement, constexpren C++11 / if constexpren C++17, il est possible de calculer ces valeurs directement à l'aide d'une fonction au moment de la compilation :

modèle < unsigned N > factorielle non signée () { si constexpr ( N ​​<= 1 ) renvoie 1 ; sinon renvoie N * factorielle < N -1 > (); }

Pour cette raison, la métaprogrammation de modèles est désormais principalement utilisée pour effectuer des opérations sur les types.

Plus d articles de Worldlex Wiki

Revenez a l index pour explorer davantage de pages sur l histoire, la science, la culture, la geographie et la societe en francais.

Explorer l index