lundi 28 septembre 2009

Ne vous inquiétez pas, je devrais arriver avec des choses plus intéressantes dans les jours à venir (si j'ai le temps)...

Les templates de C++ permettent d'écrire du code générique, c'est à dire de se faire chier une seule fois pour ensuite pouvoir utiliser le même code partout. Enfin, c'était là leur but premier, et puis un gars (David Vandevoorde, si je me souviens bien) a découvert qu'elles étaient turing complete. Cela signifie qu'on peut écrire, avec des templates, des programmes qui s'exécuteront à la compilation.

L'exemple le plus répandu est une factorielle en O(1):


#include <iostream>

template<int N>
struct Factorial
{
static const int Value = N * Factorial<N - 1>::Value;
};

template<>
struct Factorial<0>
{
static const int Value = 1;
};

int main()
{
std::cout << Factorial<6>::Value; // affiche 720
}
La valeur de la factorielle sera évaluée à la compilation, et sera équivalente à une constante.

On peut également créer des assertions statiques (des tests qui font planter la compilation s'ils ne passent pas) avec des templates. À noter qu'une telle pratique sera rendue obsolète en C++0x, qui inclura un support standard pour de telles assertions.

En Java, pour éviter les abus de downcasts de Object à quelque chose, on a introduit dans la version 1.5 les Generics. C# a eu tôt fait de copier le concept dans sa version 2.0. Mis à part une syntaxe légèrement différente pour les contraintes, la principale nuance entre C# et Java est qu'en Java, les Generics sont remplacés par des Object avec des casts, alors qu'en C#, ils sont remplacés par le type utilisé. Ces deux approches se valent, bien que je préfère celle de C#: elle permet, entre autres, d'effectuer de la réflexion sur les types génériques.

Pour celui qui connaît peu les templates de C++, on serait porté à croire que les Generics sont très semblables, mais il y a énormément de différences entre les deux. La plus importante est que les templates sont instanciés à la compilation, et le compilateur peut donc évaluer immédiatement si elles sont correctement utilisées (et si ce n'est pas le cas, générer un message d'erreur incompréhensible). En Java et en C#, les generics sont instanciés à l'exécution, et le compilateur ne peut donc pas s'assurer qu'elles sont bien utilisées. Le programmeur doit donc insérer des "contraintes", sinon, le type générique sera considéré comme un Object, ce qui tue un peu le principe. De telles contraintes sont inutiles en C++, car les templates répondent au principe de SFINAE.

Pourquoi une telle discussion? J'ai trouvé un vieil article sur MSDN décrivant les différences entre les templates de C++ et les Generics de C#:
http://msdn.microsoft.com/en-us/library/c6cyy67b(loband).aspx
En passant, pour ceux qui ne le savent pas, le "(loband)" dans l'URL, c'est pour activer le "low bandwidth mode" sur MSDN, vous permettant de sauver de la bande passante.

Voilà.

1 commentaires:

Patrice Roy a dit…

Allo Étienne!

Celui qui s'est aperçu du caractère Turing Complete des templates est, à ma connaissance, Erwin Unruh. Vandervoorde en parle dans http://www.informit.com/articles/article.aspx?p=30667 si tu veux des détails.

Bizous!

Enregistrer un commentaire