Salut les agrumes ! Bienvenue dans ce nouvel épisode de Causerie++. Pour les nouveaux, vous pouvez trouver l’épisode précédent ici.
Cet épisode a une semaine de retard, je m’en excuse. J’étais trop occupé personnellement ces derniers temps, je n’ai pas réussi à le sortir la semaine dernière.
Trêve de mondanités, aujourd’hui, on va enfin parler des Concepts !
[C++20] Concepts
Les Concepts sont une nouvelle fonctionnalité qui rend l’utilisation de templates beaucoup plus agréable qu’avant. Et personnellement, j’adore les templates, donc ça me plaît beaucoup !
L’idée des concepts est de rendre moins générique les paramètres templates. En effet, en C++, on peut soit déclarer une fonction « classique », soit déclarer une fonction template avec decs paramètres complètement génériques. Avec les Concepts, on pourra restreindre les paramètres templates pour qu’ils respectent certaines conditions.
Cela a plusieurs conséquences très agréables.
Des erreurs de compilation bien plus claires
On reproche souvent aux templates de générer des erreurs absoluement incompréhensibles. Avec les concepts, beaucoup de ces erreurs seront remplacés par de jolis :
error: cannot call std::sort with std::_List_iterator<int>
note: concept RandomAccessIterator<std::_List_iterator<int>> was not satisfied
Cet exemple est tiré de cette page.
Des interfaces plus claires
Ce second point est fortement corrélé au premier. grâce aux Concepts, les conditions que doivent respecter nos paramètres templates ne sont plus implicites ; elles font directement partie de l’interface de nos fonctions templates. Ainsi, en plus d’avoir des erreurs plus claires, les interfaces elles-mêmes deviennent limpides.
Simplification de spécialisations
Grâce aux Concepts, on pourra simplifier des spécialisations nécessitant de faire du SFINAE en les remplaçant par de simples surcharges basées sur des Concepts. En plus de rendre le code plus facile à écrire, cela le rend surtout beaucoup plus expressif.
Franchement, qui préfère ceci :
template<typename IterT>
IterT increment_impl(IterT iter, int n, std::forward_iterator_tag)
{
assert(n >= 0);
for(int i = 0; i < n; ++i)
++iter
return iter;
}
template<typename IterT>
IterT increment_impl(IterT iter, int n, std::random_access_iterator_tag)
{
return iter + n;
}
template<typename IterT>
IterT increment(IterT iter, int n)
{
return increment_impl(iter, n, typename std::iterator_traits<IterT>::iterator_category{});
}
A cela :
template<ForwardIterator IterT>
IterT increment(IterT iter, int n)
{
assert(n >= 0);
for(int i = 0; i < n; ++i)
++iter
return iter;
}
template<RandomAccessIterator IterT>
IterT increment(IterT iter, int n)
{
return iter + n;
}
La version « Concepts » est clairement plus expressive, on dirait vraiment une simple surcharge sur le type d’itérateurs. Personnellement, voir un code aussi expressif, ça m’émeut énormément !
Quelques ressources
Je vous conseille de lire les C++ Core Guidelines concernant l’utilisation de Concepts ; cela donne de bons conseils de base pour les utiliser correctement.
A ce propos, je me demande si ça vaut le coup de lire les C++ Core Guidelines en entier. Quelqu’un l’a-t-il déjà fait ? Qu’en pensez-vous ?
J’ai aussi vu une conférence intéressante sur les Concepts il y a quelques semaines, vous pouvez la trouver ici.
Pour résumer, les Concepts sont pour moi la notion qui manquait pour compléter le système des templates. Ils résolvent de nombreux défauts des templates, et nous donnent de nouveaux outils pour avoir du code plus simple à écrire et à comprendre. C’est clairement, avec les Contrats, ce qui me plaît le plus dans cette nouvelle norme !
Et voilà pour cet épisode ! Il est un peu plus court que les précédents, j’en suis désolé ; mais je pense que ça va être comme ça pendant encore quelques semaines, car je suis très occupé en ce moment !
A lundi prochain ! <3