FAQ C++/CLI et VC++.NetConsultez toutes les FAQ

Nombre d'auteurs : 29, nombre de questions : 248, création le 22 février 2013 

 
OuvrirSommaireLe langage C++/CLILes types et fonctions génériques

Les génériques du C++/CLI (mot clé generic) ressemblent beaucoup aux templates du C++.
Ils sont quand même subtilement différents dans la mesure où les génériques CLI sont instanciés par le VES (Virtual Execution System) au moment de l'exécution plutôt qu'à la compilation pour les templates.
Une définition générique doit être :

  • une classe référence (ref class).
  • une classe de valeur (value class).
  • une interface (interface class).
  • un délégate.
  • Ou enfin une fonction.
Créé le 9 mai 2006  par nico-pyright(c)

Lien : http://cpp.developpez.com/faq/cpp/?page=templates

Le C++ supporte les templates, pourquoi choisir les génériques ?

Pour pouvoir choisir, il est important de connaître certaines des différences entre ceux-ci.

Les templates sont instanciés à la compilation alors que les generics sont instanciés au moment de l'exécution. Ce qui implique que l'on peut spécialiser un generic dans un autre assembly, ce que l'on ne pourra pas faire avec un template. En effet ces derniers au moment de l'exécution ne sont plus des types paramétrables. Ainsi, deux generics spécialisés dans deux assemblys différentes avec le même type d'arguments correspondront au même type. A contrario de deux templates qui seront considérés comme deux types différents.

Cela s'explique parce que les generics de type référence génèrent une seule portion de code valable pour tous les types d'arguments. Les tempates (ainsi que les generic de type de valeur) généreront une portion de code pour chaque spécialisation.
Le compilateur est aussi capable d'optimiser les generics en fonction des types en paramètres.
En revanche les templates pourront supporter des templates de templates en paramètres, alors que les generics ne le pourront pas.

 
Sélectionnez

template<template<class T> class X> class MyClass
				
Créé le 12 juillet 2006  par nico-pyright(c)

Pour créer une fonction générique, on utilise le mot clé generic sur la première ligne pour indiquer que nous faisons une définition générique.
Le mot clé typename entre crochets <> indique que le type du paramètre est T dans la fonction générique. Ce type sera remplacé par le type utilisé au moment de l'utilisation de la fonction.
On peut utiliser plusieurs paramètres en séparant les types dans les crochets par des virgules.

Imaginons que nous voulions faire une fonction qui énumère des éléments d'un tableau CLI :

 
Sélectionnez

generic<typename T>
void afficheTableau(array<T> ^ tab)
{
	for each(T i in tab)
		if (i)
			Console::Write(i->ToString() + " ");
	Console::WriteLine();
}				
				

On pourra appeler cette fonction par exemple de la sorte :

 
Sélectionnez

array<String^> ^s = {"1", "2", "4", "6", "8"};
afficheTableau(s);
array<int> ^i = {1, 2, 4, 6, 8};
afficheTableau(i);				
				

Cela peut aussi fonctionner pour une classe perso, dans la mesure où celle-ci implémente dans notre cas la méthode ToString() :

 
Sélectionnez

ref class Personne
{
private:
	String ^ nom;
	String ^ prenom;
public:
	Personne(String ^n, String ^p)
	{
		nom = n;
		prenom = p;
	}
	virtual String ^ ToString() override
	{
		return nom + " " + prenom;
	}
};
 
array<Personne^> ^p = gcnew array<Personne ^>(3);
p[0] = gcnew Personne("nico", "pyright(c)");
p[1] = gcnew Personne("Herb", "Sutter");
afficheTableau(p);				
				

Remarque : Au niveau de la syntaxe il est aussi bien correct d'écrire :

 
Sélectionnez

array<int> ^i = {1, 2, 4, 6, 8};
afficheTableau<int>(i);
				

que

 
Sélectionnez

array<int> ^i = {1, 2, 4, 6, 8};
afficheTableau(i);
				

Dans le second cas, le type du paramètre est déduit à partir de i.

Mis à jour le 12 juillet 2006  par nico-pyright(c)

Voici un exemple de classe générique, qui reproduit le fonctionnement (basique) d'une pile :

 
Sélectionnez

generic<typename T>
public ref class Pile
{
	array<T>^ elements;
	int curElement;
public:
	Pile(int taille) 
	{
		elements = gcnew array<T>(taille);
		curElement = 0;
	}
	void Push(T elt)
	{
		if (curElement > elements->Length -1)
			throw gcnew Exception("Il n'y a plus de place dans la pile");
		else
		{
			elements[curElement] = elt;
			curElement++;
		}
	}
	T Pop()
	{
		if (curElement>0)
		{
			curElement--;
			return elements[curElement];
		}
		throw gcnew Exception("Il n'y a plus d'éléments dans la pile");
	}
};	
				

Pour l'utiliser, il suffit de l'instancier ainsi :

 
Sélectionnez

	Pile<int>^ p = gcnew Pile<int>(10);
	try
	{
		p->Push(1);
		p->Push(2);
		p->Push(3);
		Console::WriteLine(p->Pop());
		Console::WriteLine(p->Pop());
		p->Push(4);
		Console::WriteLine(p->Pop());
		Console::WriteLine(p->Pop());
	}
	catch (String ^e)
	{
		Console::WriteLine(e);
	}	
				

Pour utiliser la pile avec un autre type que int, il suffirait de l'instancier ainsi :

 
Sélectionnez

Pile<MonObjet^>^ p = gcnew Pile<MonObjet^>(10);
p->Push(gcnew MonObjet);
MonObjet^ m = p->Pop();				
				
Créé le 9 mai 2006  par nico-pyright(c)

On procède de la façon que pour Comment créer une fonction générique ?, en utilisant le mot clé where.
La contrainte se fait sur l'utilisation d'une interface.
Dans notre exemple, nous n'accepterons uniquement que des types en premier paramètre qui implèmentent l'interface IEnumerable (notamment le for each) et qui implémentent IComparable pour le deuxième paramètre (notamment la méthode Equals) :

 
Sélectionnez

generic<typename T1, typename T2> where T1:IEnumerable where T2:IComparable
int donnePosition(T1 x, T2 y)
{
	int i=0;
	for each(T2 t in x)
	{
		if(y->Equals(t))
			return i;
		i++;
	}
	return -1;
}			
				

Cette fonction nous retourne la position d'un élément dans un tableau. On pourra l'appeler ainsi :

 
Sélectionnez

array<int> ^a = {4, 5, 11, 2, 3};
Console::WriteLine(donnePosition(a,11));
array<String ^> ^s = {"un", "deux", "trois", "quatre", "cinq"};
Console::WriteLine(donnePosition(s,"cinq"));
 
ArrayList ^g = gcnew ArrayList();
g->Add("un");
g->Add("deux");
Console::WriteLine(donnePosition(g,"un"));
				

Vous pouvez constater que cette fonction marche aussi bien pour des array CLI que des ArrayList .Net qui implémentent tous les deux IEnumerable.
Remarque : On obtient une erreur de compilation si les types ne respectent pas les contraintes :

 
Sélectionnez

'monObjet ^' : invalid type argument for generic parameter 'T2' of generic 'donnePosition', does not meet constraint 'System::IComparable ^'
        		
Créé le 9 mai 2006  par nico-pyright(c)
  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2006-2007 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.