Caractéristiques de C++ – quelques liens pertinents

Cette section est touffue du fait (a) qu'on parle d'un langage très riche et très puissant et (b) parce que le sujet m'intéresse particulièrement, il faut bien l'avouer. La liste qui suit n'est pas exhaustive; c'est simplement ce que j'ai eu le temps d'assembler...

CaractéristiqueDepuis...

Argument-Dependent Lookup (ADL)

C++ 98

Algorithmes

C++ 98

Alias avec using

C++ 11

Allocateurs

C++ 98, mais mis à jour de manière importante avec C++ 11

Allocateurs munis d'une portée (Scoped Allocators)

C++ 11

Opérateurs alignas et alignof

C++ 11

Annotations [[noreturn]] et [[carries_dependency]]

C++ 11

Annotation [[deprecated]]

C++ 14

Annotations [[fallthrough]], [[maybe_unused]] et [[nodiscard]]

C++ 17

Type any

C++ 17

Assertions statiques (static_assert)

C++ 11

Opérations atomiques

C++ 11

Mot clé auto

C++ 11, mais il s'agit d'une très ancienne caractéristique de C++

std::bind()

C++ 11

std::byte

C++ 17

Les concepts

C++ 20, du moins on l'espère

Expressions constantes généralisées (mot clé constexpr)

C++ 11

Constructeurs de délégation

C++ 11

Constructeurs exposés du parent

C++ 11

Conteneurs et itérateurs

C++ 98

Déclarations a priori (Forward Declarations)

C++ 98

Opérateur decltype(expr)

C++ 11

Opérateur decltype(auto)

C++ 14

« Fonction » declval<T>()

C++ 11

Fonctions =default et =delete

C++ 11

Opérations emplace()

C++ 11

Énumérations fortes

C++ 11

Espaces nommés

C++ 98, mais C++ 11 pour les espaces nommés inline et C++ 17 pour les écritures concises d'espaces nommés imbriqués

Exceptions et spécifications d'exceptions

C++ 98

Mot clé explicit

C++ 98, mais C++ 11 pour les opérateurs de conversion et C++ 20 pour la version conditionnelle

Expressions régulières

C++ 11

Mot clé contextuel final

C++ 11

Flux

C++ 98

Foncteurs

C++ 98

Fonctions inline

C++ 98

Variables inline

C++ 17

Boucles for sur des intervalles

C++ 11

Raffinements au mot clé friend

C++ 11

Le type std::function

C++ 11

Tables de hashage

C++ 11

Héritage

C++ 98

if/switch avec initialisation

C++ 17

if constexpr

C++ 17

Initialisation immédiate des attributs d'instance (NSDMI)

C++ 11

Initialisation uniforme

C++ 11

Internationalisation

C++ 98

Expressions λ

C++ 11

Listes d'initialisation (std::initializer_list)

C++ 11

Littéraux binaires

C++ 14

Littéraux maison

C++ 11

Littéraux texte bruts

C++ 11

Mémoire (la gérer)

C++ 98

Méthodes qualifiées & et &&

C++ 11

Métaprogrammation

C++ 98

Clause noexcept

C++ 11

nullptr

C++ 11

Opérateurs (et leur surcharge)

C++ 98

Opérateurs de conversion explicites

C++ 11

optional<T>

C++ 17

Mot clé contextuel override

C++ 11

Pointeurs intelligents

C++ 98

Préprocesseur

C++ 98

Références sur des rvalue

C++ 11

Séparateurs d'unités

C++ 14

Standardisation de l'initialisation

C++ 11

Affectation déstructurante (Structured Bindings)

C++ 17

Surcharge de fonctions

C++ 98

Syntaxe unifiée des fonctions

C++ 11

Les templates

C++ 98

Les template typedefs

C++ 11

Les templates variadiques

C++ 11

Transtypage

C++ 98

Les uplets, ou tuples

C++ 11

Types

C++ 98

Variables templates

C++ 17

Type variant

C++ 17

Outils et compilateurs

C++ 98

Trucs techniques d'ordre général

C++ 98

Unification des syntaxes d'appels de fonctions et de méthodes

C++ 23?

Bibliothèque <chrono>

C++ 11

Bibliothèque <random>

C++ 11

Bibliothèque <type_traits>

C++ 11

Pour d'autres sites du même acabit :

alignas et alignof

J'ai regroupé les considérations quant à l'alignement en mémoire dans ../Sujets/Developpement/Alignement.html

Algorithmes

Alias avec using

Depuis C++ 11, il est possible de remplacer les alias traditionnellement faits avec typedef par des alias faits avec using. Concrètement, par rapport à typedef, using n'a que des avantages, permettant de faire tout ce que son prédécesseur permettait, de le faire mieux, et de faire plus encore. Quelques exemples suivent.

Avec using (depuis C++ 11) Avec typedef (avant C++ 11)
using quantite = unsigned int;
typedef unsigned int quantite;
using ptri = int*;
typedef int * ptri;
using ptrf = int(*)(double);
typedef int (*ptrf)(double);
template <class V>
   using dictionnaire = map<string, V>;

Pas d'équivalent (pas de moyen pour définir partiellement un template)

template <class It>
   using val_t = typename iterator_traits<It>::value_type;

Pas d'équivalent (on faire un alias comme val_t pour une valeur de It choisie, mais pas un val_t paramétrique sur la base de It)

Quelques textes :

Allocateurs

Voir Gestion-memoire--Liens.html#allocateur pour des détails.

Allocateurs munis d'une portée (Scoped Allocators)

Voir Gestion-memoire--Liens.html#allocateur pour des détails.

Annotations

Voir ../Sujets/Divers--cplusplus/annotations.html pour des détails.

any

Les informations ont été regroupées dans ../Sujets/Divers--cplusplus/optional.html

Argument-Dependent Lookup

L'Argument-Dependent Lookup (ADL), qu'on nomme parfois aussi le Koenig Lookup (pour Andrew Koening). Un truc à la fois utile et pas simple... Voir ../Sujets/Divers--cplusplus/argument_dependent_lookup.html pour plus d'informations.

auto et decltype

Voir ../Sujets/Divers--cplusplus/deduction_types.html#auto pour des détails sur auto

Voir ../Sujets/Divers--cplusplus/deduction_types.html#decltype_expr pour des détails sur decltype(expr)

Voir ../Sujets/Divers--cplusplus/deduction_types.html#decltype_auto pour des détails sur decltype(auto)

Voir ../Sujets/Divers--cplusplus/deduction_types.html#declval pour des détails sur declval<T>()

Bibliothèque <chrono>

Des éléments de la nouvelle bibliothèque d'outils de mesure du temps, <chrono> (enfin!) :

Bibliothèque <random>

Des éléments de la nouvelle bibliothèque stochastique, <random> (un ajout très apprécié au standard du langage!) :

Bibliothèque <type_traits>

Enfin, des traits sur les types, de manière standard :

std::bind()

Bien qu'en grande partie supplanté par les expressions λ, la fonction std::bind() permet d'associer des paramètres à un appel de fonction, pour par exemple répéter un paramètre, permuter leur position, transformer une fonction binaire (à deux opérandes) en une fonction unaire (à un seul paramètre) avec l'autre paramètre fixé, etc.

Pour un exemple simple :

#include <functional>
#include <iostream>
using namespace std;
void afficher(int a, double b, const char *c) {
   cout << a << ' ' << b v< ' ' << c << endl;
}
int main() {
   using namespace std::placeholders;
   afficher(2, 3.5, "J'aime mon prof!");
   auto rev = bind(afficher, _3, _2, _1);
   rev("Yo", 3.5, 3); // les paramètres sont passés en ordre inverse
   auto f = bind(afficher, 3, 3.1459f, "J'aime encore mon prof!!!");
   f(); // les trois paramètres sont fixés
   auto g = bind(afficher, _1, _1, "J'aime encore mon prof!!!");
   g(7); // afficher(7, 7, "J'aime encore mon prof!!!");
}

std::byte

Le type std::byte vise à remplacer, éventuellement, le recours à char en tant que représentation d'un byte, pour redonner à char son rôle de représentation d'un caractère. Notez que std::byte n'est pas un type arithmétique (bien qu'il admette des opérations bit à bit); son rôle est de modéliser un espace d'entreposage.

Notez que, sur le plan historique, de nombreux types nommés byte existent dans des en-têtes de plateformes (par exemple, un alias byte est défini dans <windows.h>), alors il est préférable d'éviter using namespace std; si vous incluez des en-têtes de plateformes, pour éviter les conflits de noms avec ce type.

Concepts

Les informations sur les concepts ont été regroupées sur ../Sujets/Divers--cplusplus/Concept-de-concept.html

constexpr

Expressions constantes généralisées, ou constexpr : ../Sujets/Divers--cplusplus/constexpr.html

Constructeurs de délégation

Il est possible depuis C++ 11 de faire en sorte qu'un constructeur délègue son travail à un autre constructeur.

Constructeurs exposés du parent

Il est possible depuis longtemps d'exposer, dans une classe dérivée, des services cachés d'un parent, à l'aide du mot-clé using. Depuis C++ 11, ce mécanisme est aussi applicable aux constructeurs.

Conteneurs et itérateurs

Les conteneurs et les itérateurs sont, avec les algorithmes standards, au coeur des pratiques contemporaines de programmation en C++.

Déclarations a priori (Forward Declarations)

J'ai relocalisé ce que j'avais à ce sujet sur un (court) article que vous trouverez sur ../Sujets/TrucsScouts/declaration_a_priori.html

Opérations emplace

Opérations emplace(). Raffinement des traditionnels insert(), push_front(), push_back() etc. des conteneurs standards de C++. L'idée est de construire les éléments à même le conteneur, plutôt que de construire une temporaire et de la copier dans le conteneur par la suite :

Énumérations fortes

À propos des énumérations fortes, un important raffinement en comparaison avec les énumérations « classiques » :

Espaces nommés

Certains disent aussi espaces de noms :

Avant C++ 17 Depuis C++ 17
namespace A {
   namespace B {
      namespace C {
         int f();
      }
   }
}
namespace A::B::C {
   int f();
}

Une question qui revient souvent, en lien avec les espaces nommés, est le recours à un espace nommé anonyme, par exemple :

namespace {
   int glob = 3; // variable globale, ::glob de son vrai nom
}

Techniquement, les éléments d'un espace nommé anonyme ne sont pas visibles à l'édition des liens. Ceci remplace en quelque sorte la qualification static apposée traditionnellement sur les fonctions et les variables qui devaient être locales à un seul module objet dans un programme C.

L'avantage des espaces nommés anonymes sur la qualification static est qu'ils permettent aussi de cacher des types à l'éditeur de liens, alors que static ne s'applique qu'aux variables et aux fonctions.

Mot clé explicit

Les informations sur les concepts ont été regroupées sur ../Sujets/Divers--cplusplus/explicit.html

Expressions régulières

À propos des expressions régulières, enfin supportées de manière standard depuis C++ 11 :

Mot clé contextuel final

Les informations sur les concepts ont été regroupées sur ../Sujets/Divers--cplusplus/final.html

Flux

À propos des flux :

Foncteurs

À propos des foncteurs (aussi nommés Function Objects) : ../Sujets/Divers--cplusplus/foncteurs.html

Fonctions inline

À propos des fonctions inline : ../Sujets/Divers--cplusplus/inline.html#fonction

Variables inline

À propos des variables inline : ../Sujets/Divers--cplusplus/inline.html#variable

Boucles for sur des intervalles

Depuis C++ 11, il est possible de remplacer une répétitive comme celle-ci :

for(vector<T>::iterator it = begin(v); it != end(v); ++it)
   f(*it);

... par une répétitive comme celle-là :

for(T &elem : v)
   f(elem);

... ou encore, de manière plus générale, comme celle-là :

for(auto &elem : v)
   f(elem);

Il est possible de manipuler ainsi les éléments de tout conteneur pour lequel les fonctions globales std::begin() et std::end() s'appliquent. Il est possible de manipuler les éléments :

Par copie :

for(auto elem : v)
   f(elem);

Par référence :

for(auto &elem : v)
   f(elem);

Par référence-vers-const :

for(const auto &elem : v)
   f(elem);

Notez que le paramètre de la boucle représentant le conteneur sur les éléments duquel on itérera ne sera évalué qu'une seule fois. Ainsi, le programme suivant :

#include <iostream>
#include <vector>
using namespace std;

vector<int> f() {
    cout << "Appel de f()" << endl;
    vector<int> v { 1,2,3,4,5 };
    v.push_back(6);
    return v;
}

int main() {
    for(auto i : f())
       cout << i << endl;
}

... offrira la sortie suivante sur tous les compilateurs contemporains :

Appel de f()
1
2
3
4
5
6

Vous conviendrez que la nouvelle forme est plus concise que celle qui l'a précédée. Les boucles for sur des intervalles simplifient certaines pratiques usitées :

Raffiner friend 

std::function 

Le type std::function qui joue en C++ le rôle des délégués en C#. Pour une introduction : ../Sujets/TrucsScouts/intro_std_function.html

Hashage

Des tables de hashage :

Héritage

L'héritage avec C++ :

if/switch avec initialisation

À partir de C++ 17, il devient possible de définir des variables à même un if ou un switch, de manière à ce que la portée de telles variables soit locale à la structure de contrôle. Par exemple :

Avant C++ 17 Depuis C++ 17
int n = f();
if(n < 0) {
   // n est accessible (bonne chose)
} else {
   // n est accessible (bonne chose aussi)
}
// n est accessible; peut être Ok, mais pas nécessairement
if(int n = f(); n < 0) {
   // n est accessible (bonne chose)
} else {
   // n est accessible (bonne chose aussi)
}
// n n'est plus accessible
char c = lire_touche();
switch(c) {
case '+' :
case '-' :
case '*' :
case '/' :
case '%' : cout << c << " est un operateur arithmetique" << endl; break;
// ...
}
// ici, c existe existe encore... peut-être Ok, peut-être pas
switch(char c = lire_touche(); c) {
case '+' :
case '-' :
case '*' :
case '/' :
case '%' : cout << c << " est un operateur arithmetique" << endl; break;
// ...
}
// ici, c n'existe plus

Ceci est particulièrement sympathique en combinaison avec les Structured Bindings :

Avant C++ 17 Depuis C++ 17 Depuis C++ 17 avec Structured Bindings
std::pair<std::string, bool> mot();
void afficher_si_ok() {
   auto p = mot();
   if(p.second) // ok d'afficher le mot
      cout << p.first << endl; // le faire
}
std::pair<std::string, bool> mot();
void afficher_si_ok() {
   if(auto p = mot(); p.second) // ok d'afficher le mot
      cout << p.first << endl; // le faire
}
std::pair<std::string, bool> mot();
void afficher_si_ok() {
   if(auto [s,ok] = mot(); ok) // ok d'afficher le mot
      cout << s << endl; // le faire
}

À ce sujet :

if constexpr

Les informations ont été regoupées dans ../Sujets/Divers--cplusplus/if_constexpr.html

Initialisation immédiate des attributs d'instance (non-static data member initialization, NSDMI)

Les informations ont été regroupées dans ../Sujets/Divers--cplusplus/NSDMI.html

Initialisation uniforme

Les informations ont été regoupées dans ../Sujets/AuSecours/initialisation-uniforme.html

Internationalisation

L'internationalisation (on écrit aussi i18n, car il y a 18 lettres entre le « i » et le « n ») :

Expressions lambdas (λ)

Les λ, qui allègent tellement les tâches courantes... Voir ../Sujets/Divers--cplusplus/Lambda-expressions.html pour des détails

Listes d'initialisation ( std::initializer_list)

J'ai groupé l'information à ce sujet dans ../Sujets/Divers--cplusplus/initializer_lists.html

Littéraux binaires

Ils est maintenant possible d'exprimer des littéraux binaires en C++. Par exemple, les expressions ci-dessous décrivent le même nombre :

Décimal Décimal avec séparateurs Octal Hexadécimal Binaire
1234567890
1'234'567'890
11145401322
499602d2
01001001100101100000001011010010

Littéraux « maison »

J'ai regroupé dans ../Sujets/Divers--cplusplus/litteraux_maison.html l'information sur les littéraux maison, par lesquels il devient possible d'enrichir la gamme des littéraux du langage.

Littéraux textes bruts

Les littéraux texte bruts, très utiles à l'ère Internet, entre autres pour manipuler du texte balisé et des expressions régulières :

À titre d'exemple, ceci... ...est équivalent à cela
"yo\n\\t'sais\n\"genre\" style\n\ncomme"
R"(yo
\t'sais
"genre" style

comme)"

Ces deux écritures décrivent le même const char*. On peut préférer l'un ou l'autre, mais les deux options existent.

Mémoire (gestion)

Considérations de gestion de mémoire avec C++ :

Métaprogrammation

La métaprogrammation est un sujet chaud dans ce langage :

Méthodes qualifiées & et &&

Il est possible depuis C++ 11 d'appliquer les qualification & et && à des méthodes, en plus des qualifications const et volatile.

noexcept

La clause noexcept : ../Sujets/Developpement/Exceptions.html#noexcept

Opérateurs

Notez qu'il est très facile d'abuser de la surcharge d'opérateurs, etr certains des liens qui suivent donnent des trucs qui pourraient mener à du code plus difficile à utiliser ou brisant le principe de moindre surprise. Agissez avec discrimination, et souvenez-vous que le code est lu plus souvent qu'il est écrit!

Les opérateurs et leur surcharge :

Opérateurs de conversion explicites

Les informations ont été regroupées dans ../Sujets/Divers--cplusplus/explicit.html#operateur_conversion

optional<T>

Les informations ont été regroupées dans ../Sujets/Divers--cplusplus/optional.html

Que veut-on dire par « mot-clé contextuel »? Voici un exemple qui pousse l'idée vers l'absurde (emprunté à Adi Shavit dans https://twitter.com/AdiShavit/status/913758314009415681) :

Mot clé contextuel override

Le mot clé contextuel (et optionnel) override permet d'expliciter l'intention, dans une classe dérivée, de spécialiser une méthode polymorphique d'une classe parent.

Pointeurs intelligents

J'ai groupé les informations portant sur les pointeurs intelligents dans ../Sujets/AuSecours/pourquoi_pointeurs_intelligents.html

Préprocesseur

Vous trouverez d'autres liens sur ce sujet dans la section commune aux langages C et C++.

Le préprocesseur est une chose à éviter en C++, mais parfois... :

Références sur des rvalue

Les références sur des rvalue (rvalue references) :

Séparateurs d'unités

Ils est maintenant possible d'exprimer des littéraux binaires en C++. Ceci peut améliorer la lisibilité, du moins si on l'utilise judicieusement. La séparateur utilisé est l'apostrophe. Ainsi, les écritures suivantes sont équivalentes :

Décimal Décimal avec séparateurs Octal Octal avec séparateurs Hexadécimal Hexadécimal avec séparateurs Binaire Binaire avec séparateurs
1234567890
1'234'567'890
011145401322
011'145'401'322
0x499602d2
0x49'96'02'd2
0b01001001100101100000001011010010
0b0100'1001'1001'0110'0000'0010'1101'0010

Standardisation de l'initialisation

À propos de la standardisation de l'initialisation, qui rapproche le langage de l'un de ses objectifs, soit celui de traiter tous les types sur un même pied :

Affectation déstructurante (Structured Bindings)

Avec C++ 17, il devient possible de déconstruire un struct ou un tuple retourné par une fonction en ses éléments constitutifs, de manière à faciliter l'accès à ces membres dans le contexte du code client. Par exemple :

Avant C++ 17 À partir de C++ 17
struct Point {
   int x, y;
};
const int LARGEUR_CARTE = 80;
// ...
Point voisin_ouest(const Point&);
// ...
bool peut_aller_ouest(const Point &pt) {
   auto prochain = voisin_ouest(pt);
   return prochain.y < LARGEUR_CARTE;
}
struct Point {
   int x, y;
};
const int LARGEUR_CARTE = 80;
// ...
Point voisin_ouest(const Point&);
// ...
bool peut_aller_ouest(const Point &pt) {
   auto [x,y] = voisin_ouest(pt);
   return y < LARGEUR_CARTE;
}

À ce sujet :

Lors de la rencontre du WG21 à Kona en 2017, nous avons discuté d'une dichotomie entre le texte du Core Language, qui utilisait le terme Decomposition Declarations pour l'expression en soi et le terme Structured Bindings pour les variables découlant de cette décomposition, car il y avait une volonté d'utiliser strictement Structured Bindings dans le texte pour réduire la confusion des programmeuses et des programmeurs lorsque celles ou ceux-ci verront apparaître un message d'erreur à la compilation.

Après quelques débats, mettant entre autres en valeur l'importance de distinguer l'expression et son résultat, nous avons convenu d'utiliser Structured Bindings et Structured Binding Declarations. Ces termes devraient être ceux que vous rencontrerez en pratique, mais sachez que Decomposition Declarations a été utilisé pendant un certain temps pour ce qui sera désormais nommé Structured Binding Declarations.

Surcharge de fonctions

C++ permet de distinguer deux fonctions sur la base d'un certain nombre de critères. En effet, dans ce langage, deux fonctions sont différentes si :

Cette caractéristique du langage complique quelque peu l'interopérabilité au niveau du code machine généré : les noms dans le code machine sont typiquement différents du nom dans le code source; en ce sens, interopérer avec du code écrit en langage C, où il n'est pas légal d'avoir deux fonctions distinctes du même nom, est plus simple. Il est toutefois possible de faire de petits miracles à l'aide de cette mécanique. Notez que plusieurs utilisent enable_if pour contrôler la sélection de fonctions par le compilateur lorsque celui-ci cherche à déterminer laquelle des fonctions serait la plus à propos pour un certain appel.

Syntaxe unifiée des fonctions

Il est possible depuis C++ 11 d'écrire une fonction dont le type suit la signature plutôt que de la précéder... Et c'est parfois très utile!

Les template typedefs

Il est possible depuis C++ 11 de définir des templates avec spécialisations partielles, ce qui permet d'alléger énormément certaines écritures lourdes (en particulier celles en lien avec les traits) :

Les templates variadiques

Les templates variadiques permettent de suppléer un nombre arbitrairement grand de paramètres à une fonction :

Transtypage

Le transtypage, aussi nommé conversions explicites de type ou encore – en anglais – les Type Casts, ou simplement Casts :

tuple

Les tuples, ou uplets en français :

Types

Questions de types, en particulier les types primitifs du langage :

Variables templates

Depuis C++ 14, il est possible de définir des variables génériques (en plus des types et des fonctions génériques, bien connues des programmeuses et des programmeurs C++ de manière générale). Ceci permet d'exprimer du code générique tel que :


template <class T>
   constexpr const T PI = T(3.1415926535897932384626433832795);
template <class T>
   T circonference_cercle(T rayon) {
      return T(2) * PI<T> * rayon;
   }

Textes d'autres sources :

À propos des mots clés contextuels

Depuis C++ 11, certains mots clés de C++ sont contextuels, au sens où ils ne sont des mots clés que lorsque placés à certains endroits dans un programme. Ceci est dû au risque élevé que ces mots apparaissent ailleurs dans du code existant. C++ est décrit par un standard ISO et qu'il ne s'agit pas d'un produit appartenant à une entreprise ou l'autre, briser le code existant lors d'une mise à jour du standard est une préoccupation importante du comité de standardisation.

Les mots clés contextuels incluent override et final.

Pour en savoir plus :

variant<T0,T1,...,TN>

Les informations ont été regroupées dans ../Sujets/Divers--cplusplus/optional.html

Unification des syntaxes d'appels de fonctions et de méthodes

Chacun à sa manière, en 2014, Bjarne Stroustrup et Herb Sutter ont proposé d'unifier (avec nuances de part et d'autre) les syntaxes obj.f(args) et f(obj,args), ce qui refléterait la pratique existante pour des cas comme begin(conteneur) et conteneur.begin().


Valid XHTML 1.0 Transitional

CSS Valide !