Bonjour à tous, aujourd’hui un nouvel article de la série L’arrivée du C++17. Nous allons traiter les différents éléments qui ont été supprimés du langage.
L’article d’il y a deux semaines sur L’arrivée du C++17 – Les éléments dépréciés avait conclu sur certains points qui allaient disparaître avec C++20. Le C++17 a apporté son lot de suppressions au langage, que je vous propose de regarder un peu plus en détail.
register
Le mot-clef register avait déjà été déprécié avec C++11, la P0001 propose donc sa suppression du langage.
En effet, si register fait effectivement partie de la grammaire, son utilisation n’a aucun effet. Le document rappelle que son usage existe en C, mais que de toute façon c’est déjà un point d’incompatibilité entre C et C++.
Cette suppression était attendue depuis que le mot-clef avait été déprécié en C++11 et ne devrait pas avoir d’impact majeur. Le mot-clef register est maintenant inutilisé et réservé pour un usage futur.
operator++(bool)
La P0002 apporte la suppression de l’opérateur de post-incrémentation pour le type bool.
La dépréciation de cette opération date de C++98, ça commence donc à dater un peu. Durant le développement de C++14, il a été montré que la post-incrémentation sur les booléens pouvait avoir des cas d’usage, comme dans cet exemple :
1 2 3 4 5 6 7 8 9 10 |
bool b{ false }; while(condition) { if(b++) { // (1) /* ... */ } /* ... */ } |
Ici, la condition (1) n’est vraie qu’une seule fois. Lors de la première itération de la boucle, b est false et on entre dans la condition. La post-incrémentation garantie de passer le booléen à true, tout en retournant sa valeur initiale.
C’est une technique assez pratique qui a poussé la création de la fonction std::exchange, apparue avec la N3668 pour C++14, et qui est destinée à remplacer cet usage :
1 2 3 4 5 6 7 8 9 10 |
bool b{ false }; while(condition) { if(std::exchange(b, true)) { // (1) /* ... */ } /* ... */ } |
C++17 enlève donc la possibilité d’utiliser la post-incrémentation. Cette suppression n’a pas beaucoup d’impact sur notre code en tant que développeur C++, même si ça fait un point de plus sur lequel le C++ et le C divergent. D’un autre côté, comme le rappelle le document, le C++ refusait déjà operator--(bool) qui est pourtant valide en C après inclusion de <stdbool.h> ( bool est un simple typedef sur un entier en C).
Les trigraphes
Pour ceux qui ne les connaissent pas, les trigraphes sont hérités du langage C et de l’époque où certains claviers ne possédaient pas un jeu de caractères couvrant tous les symboles du langage (notamment avec l’absence des crochets ou des accolades). Il s’agit de tokens de trois caractères (contrairement aux digraphes qui n’en contiennent que deux), remplacés par le préprocesseur avant la compilation.
Un exemple classique de problème avec les trigraphes est le suivant :
1 2 |
// Is this correct ??/ std::cout << myComplexOperation() << '\n'; |
Ici, la seconde ligne ne sera jamais exécutée, car ??/ est traduit en \, et la ligne suivante est donc accolée à la précédente. Le flux de sortie est donc dans un commentaire.
Il a été décidé avec la N4086 de se débarrasser des trigraphes qui ne servent plus à grand chose de nos jours (même si IBM a fermement protesté contre cette suppression pendant des années, notamment avec la N2910 et la N4210). Les digraphes restent présents dans le langage car ils ne sont pas remplacés dans les chaînes de caractères et les commentaires et sont donc moins susceptibles de produire ce genre de problèmes. Notez que les compilateurs continueront certainement à proposer les trigraphes de manière optionnelle.
Pour le plaisir, un petit Hello World ! avec des trigraphes, parce qu’ils vont nous manquer !
1 2 3 4 5 6 |
??=include <iostream> int main(int argc, char *argv??(??)) ??< std::cout << "Hello world !\n"; ??> |
ios aliases
La P0004 a amené la suppression des aliases des flux d’entrée-sorties. Ceux-ci étaient déjà dépréciés depuis C++98 et sont extrêmement peu utilisés puisqu’il s’agit d’aliases sur d’autres membres existants.
auto_ptr
std::auto_ptr avait été déprécié en C++11, et remplacé par std::unique_ptr qui profitait de certaines améliorations telles que la sémantique de mouvement et les rvalue-references pour ne pas être copiable.
La N4190 propose la suppression de std::auto_ptr, rappelant que des techniques de remplacement automatique vers std::unique_ptr, comme Replace-AutoPtr Transform de clang-modernize, existent déjà.
Les reliques de <functional>
Un certain nombre d’éléments anciens de l’entête <functional> ont été supprimés puisque des équivalents plus évolués sont maintenant disponibles.
Les binders bind1st et bind2nd, remplacés par std::bind, sont supprimés de la bibliothèque standard.
std::unary_function et std::binary_function n’ont plus vraiment d’utilité avec les ajouts de std::function et des lambdas avec de C++11.
std::mem_fun et std::mem_fun_ref sont supprimés étant donné l’introduction de std::mem_fn en C++11.
Enfin, les pointeurs de fonctions pouvant être directement passés à std::bind ou std::function, std::ptr_fun est lui aussi retiré.
random_shuffle
Une dernière suppression issue de la N4190, la suppression de std::random_shuffle, déprécié depuis C++14 :
1 2 3 4 5 |
template< class RandomIt > void random_shuffle( RandomIt first, RandomIt last ); template< class RandomIt, class RandomFunc > void random_shuffle( RandomIt first, RandomIt last, RandomFunc&& r ); |
Pour le coup la suppression arrive rapidement après la dépréciation. std::random_shuffle avait été déprécié parce qu’il autorise l’utilisation de rand() (Rand considered harmful) et n’offre donc pas de garantie sur la qualité du RNG. La surcharge std::random_shuffle(first, last, rng) impose également une interface difficile à mettre en place.
std::shuffle est donc la solution à privilégier, plus simple d’utilisation et apportant plus de garanties, ce qui a justifié une suppression si rapide de random_shuffle .
allocateurs dans std::function
std::function propose des allocateurs pour certains de ses constructeurs et fonctions membres.
La P0302 qui demande la suppression de ces surcharges, explique que l’utilisation d’un allocateur dans un contexte de type-erasure est assez complexe. Il est notamment difficile de récupérer l’allocateur pour les allocations nécessaires lors d’une affectation par copie.
De plus, le papier présente une liste des problèmes recensés avec les allocateurs de std::function et indique que les implémentations divergent grandement :
- libc++ (Clang/LLVM) ignore les allocateurs en argument.
- MSVC utilise les allocateurs à la construction mais ne les conserve pas pour l’affectation.
- GCC ne propose même pas les constructeurs avec allocateurs.
Il s’agit donc encore une fois de la suppression d’une fonctionnalité qui n’aura pas un grand impact mis à part sur certaines très vieilles codebases.
Voilà donc pour les suppressions de C++17, merci de m’avoir lu et à bientôt :).
Références et ressources
- [EN] open-std.org | P0001r1 “Remove Deprecated Use of the register Keyword”
- [EN] open-std.org | P0002r1 “Removing Deprecated operator++(bool)”
- [EN] open-std.org [ N4086 “Removing trigraphs ??!”
- [EN] open-std.org | N2910 “Comment on Proposed Trigraph Deprecation”
- [EN] open-std.org | N4210 “IBM comment on preparing for a Trigraph-adverse future in C++17”
- [EN] open-std.org | P0004R1 “Remove Deprecated iostreams aliases”
- [EN] open-std.org | N4190 “Removing auto_ptr, random_shuffle(), And Old <functional> stuff”
- [EN] GoingNative 2013 – “Rand Considered Harmful”
- [EN] open-std.org | P0302R1 “Removing Allocator Support in std::function”
You must log in to post a comment.