C++11

C++11, anciennement connu sous le nom de C++0x[1], est une norme pour le langage C++ en informatique. Elle a été approuvée unanimement le [2]. Elle remplace la précédente norme, ISO/CEI 14882, publiée en et mise à jour en . Ces dernières sont plus connues sous les noms informels de C++98 et C++03. C++11 introduit plusieurs nouveautés au langage initial, ainsi que de nouvelles fonctionnalités à la bibliothèque standard du C++ comme la plupart des bibliothèques du Technical Report 1, à l'exception de la bibliothèque de fonctions mathématiques spéciales.

C++11
Date de première version
Influencé par C++ Technical Report 1 (en)
Site web www.iso.org/standard/50372.html

C++11 a été publié sous le nom de ISO/CEI 14882:2011 en . Une version payante est disponible sur le site de l'ISO[3]. Le dernier working draft gratuit est le N3337, qui date du , les seules différences avec le standard étant des corrections éditoriales.

Un langage de programmation comme le C++ suit une évolution qui permet aux programmeurs de coder plus rapidement, de façon plus élégante et permettant de faire du code maintenable. Ce processus soulève inévitablement des questions de compatibilité avec le code existant, ce qui s'est produit de temps en temps pendant le processus de développement du C++. Cependant, d'après l'annonce faite par Bjarne Stroustrup (l'inventeur du langage C++ et membre du comité), la nouvelle norme est presque totalement compatible avec la norme précédente.

Changements prévus pour la mise à jour de la norme

Comme dit précédemment, les changements du langage C++ concernent aussi bien le langage initial que la bibliothèque standard.

Durant le développement de chaque fonctionnalité de la nouvelle norme, le comité a appliqué les directives suivantes :

  • Garder la stabilité et la compatibilité avec le C++98 et, si possible, avec le C.
  • Préférer l'introduction de nouvelles fonctionnalités par la bibliothèque standard, plutôt que par le langage lui-même.
  • Préférer les changements qui peuvent faire évoluer les techniques de programmation.
  • Améliorer le C++ pour faciliter la mise en place de systèmes et de bibliothèques, plutôt qu'introduire de nouvelles fonctionnalités seulement utiles pour des applications spécifiques.
  • Augmenter la protection des types en fournissant des alternatives plus sécurisées que les actuelles, plutôt non sécurisées.
  • Augmenter les performances et les capacités à travailler directement avec le matériel.
  • Proposer des solutions propres aux problèmes actuels.
  • Implémenter le principe du « zero-overhead » (on ne paye le coût d'une fonctionnalité que si l'on s'en sert).
  • Rendre le C++ facile à apprendre et à enseigner sans enlever les fonctionnalités requises par les programmeurs experts.

Extensions du langage

Multitâche

La mémoire locale de thread ou Thread Local Storage n'est pas un concept inventé par la nouvelle norme : de nombreux compilateurs proposent déjà cette fonctionnalité, ainsi que la bibliothèque threads de Boost. C++11 introduit le mot-clef thread_local pour déclarer qu'une variable doit être stockée dans une zone mémoire appartenant au thread. Chaque thread embarque ainsi sa propre copie d'une variable déclarée de la sorte, et les modifications d'une de ces copies n'affectent pas les copies appartenant aux autres threads. Lorsqu'une variable statique (ou une variable de classe) est ainsi définie, sa durée de vie est alors réduite à celle du thread (le destructeur des objets, notamment, est appelé lorsque le thread s'achève).

Délégation du constructeur [4]

En C++03, un constructeur appartenant à une classe ne peut pas appeler un autre constructeur de cette même classe, ce qui peut entraîner de la duplication de code lors de l'initialisation de ses attributs. En permettant au constructeur de déléguer la création d'une instance à un autre constructeur, C++11 apporte donc une solution.

class une_classe {
    int nombre;

public:
    une_classe(int nouveau_nombre) : nombre(nouveau_nombre) {}
    une_classe() : une_classe(42) {}
};

Dans l'exemple ci-dessus, on peut voir que le second constructeur appelle le premier constructeur, ce qui aurait conduit à une erreur de compilation en C++03.

Héritage du constructeur [5]

En C++03, les constructeurs d'une classe de base ne sont pas hérités par ses classes dérivées. C++11 permet d'hériter explicitement des constructeurs de la classe de base grâce à l'instruction using

class classe_base {
public:
  classe_base(int nombre) : m_nombre(nombre)

private:
  int m_nombre;
};

class classe_derive : public classe_base {
public:
  using classe_base::classe_base;
};

Initialiseurs d'attributs

En C++03, il est possible d'assigner une valeur par défaut aux variables constantes directement dans le fichier d'en-tête. C++11 étend cette possibilité aux attributs des classes. Par exemple il est désormais tout à fait possible d'écrire:

class UneClasse {
public:
    UneClasse() {}
    explicit UneClasse(int valeur) : m_valeur(valeur) {}
	
private:
    int m_valeur = 5;
};

Dans ce code, tous les constructeurs de la classe vont initialiser m_valeur à 5, si le constructeur ne remplace pas l'initialisation avec la sienne.

Par exemple, le constructeur vide ci-dessus va initialiser m_valeur selon la définition de la classe, mais le constructeur qui prend un int en paramètre initialisera m_valeur à ce paramètre.

Il est également possible d'utiliser d'autres attributs dans l'initialisation, et d'utiliser un constructeur ou l'utilisation uniforme au lieu de l'initialisation par assignation.

Sizeof sur les attributs de classes sans objet explicite

En C++03, sizeof peut être utilisé sur des types ou des objets, mais pas sur un membre de classe (excepté dans la bibliothèque Qt). C++11 le rend possible. On peut donc maintenant faire :

struct UnType { UnAutreType membre; };
 
sizeof(UnType::membre); // Ne marche pas en C++03 mais en C++11

Ce qui résultait en une erreur de compilation avant.

Liste d'initialiseurs [6]

Pour initialiser un conteneur à l'aide de valeurs connues, il fallait le faire élément par élément. C++11 introduit le patron de classe std::initializer_list qui permet d'initialiser les conteneurs avec la même syntaxe que celle permettant en C d'initialiser les tableaux, donc à l'aide d'une suite de valeurs entre accolades.

int sum(const std::initializer_list<int> &list) {
    int sum = 0;
    for (int i : list) {
        sum += i;
    }
    return sum;
}

sum({1, 2, 3, 4, 5}); // 15
sum({1, 2}) // 3

Les templates variadiques [7],[8]

Pour remplacer les fonctions variadiques du C (déconseillées en C++, car contournant toute vérification du type des paramètres), C++11 introduit les templates variadiques. Ces templates étendent le concept précédent en lui ajoutant la possibilité de prendre un nombre quelconque d'arguments. Elles sont supportées par le compilateur GCC depuis la version 4.3 car elles font partie de l'expérimentation du support de C++0x[9].

L'utilité de templates possédant un nombre quelconque d'arguments se perçoit aisément avec la classe tuple, qui généralise le concept de paire (triplet, n-uplet, etc.) ou bien avec cet exemple d'implémentation de la fonction printf :

void printf(const char *s)
{
  while (*s) {
    if (*s == '%' && *++s != '%') {
      throw std::runtime_error("too few arguments provided to printf");
    }
    std::cout << *s++;
  }
}

template<typename T, typename... Args>
void printf(const char* s, const T& value, const Args&... args) {
  while (*s) {
    if (*s == '%' && *++s != '%') {
      std::cout << value;
      printf(++s, args...);
      return;
    }
    std::cout << *s++;
  }
  throw std::runtime_error("extra arguments provided to printf");
}

C++11 définit un certain nombre de concepts que nous pouvons approcher grâce au code source suivant :

template<class ... T> struct Tuple { };

template<class ... T> 
void f(T ... args);

template<class ... T> void g(T ... a); 
template<class ... T> void h(T ... b) 
{ 
   g(b ...); 
}
  • À la première déclaration, l'argument template class ... T est appelé un pack de paramètres template car il regroupe un nombre fini d'arguments (déterminé à la compilation).
  • À la seconde déclaration, T ... args s'appelle un pack de paramètres de fonction. C'est un paramètre de fonction qui englobe un paramètre pour chaque argument contenu par le pack de paramètres template class ... T.
  • Enfin, la troisième déclaration nous apprend comment utiliser un pack étendu de paramètres. Le mot extension est utilisé car lors de l'appel à g, le pack de paramètres sera étendu avant d'être passé à g.

Les concepts

Les concepts ont été retirés de la norme.

Les chevrons (<>) [10]

Les compilateurs C++ actuels traitent toujours une séquence de deux signes supérieur à comme un opérateur de décalage binaire vers la droite. En conséquence, lors de l'imbrication de l'utilisation de patrons, les programmeurs sont obligés d'insérer un espace entre les deux chevrons fermants.

Par exemple, en C++03, ce code provoque une erreur de compilation :

#include <vector>
std::vector<std::vector<int>> matrix;
// Attention ! Écrire plutôt : “std::vector<std::vector<int> >”

C++11 tentera de détecter automatiquement si les symboles doivent jouer le rôle de chevrons fermants ou d'opérateur de décalage binaire.

Template externe [11]

Les templates ne sont actuellement pas pris en compte par l'éditeur de liens : il est nécessaire d'incorporer leur définition dans tous les fichiers sources les utilisant en programmation modulaire. Leur compilation était donc longue et gourmande puisque la classe était recompilée dans chaque fichier source, pour chaque type utilisé.

C++11 permettra l'utilisation du mot-clé extern pour rendre les templates globaux. Les fichiers désirant utiliser le template n'ont qu'à le déclarer.

Assertions statiques [12]

La bibliothèque Boost propose déjà cette facilité à travers la macro BOOST_STATIC_ASSERT. Cependant, son implémentation est étrange, basée sur la métaprogrammation et des comparaisons de taille de structures intermédiaires créées pour l'assertion sans trop de rapport avec le concept. Par conséquent, intégrer la fonction dans le langage apporte une solution propre au problème.

En pratique, une assertion statique permet de vérifier à la compilation qu'une valeur est vraie. Par exemple, il est possible d'implémenter les concepts en utilisant boost::traits et BOOST_STATIC_ASSERT. Si une classe template nécessite que son type template soit un POD (Plain Old Data), elle peut faire une assertion statique sur boost::is_pod<T>::type::value, ce qui est une constante intégrale de type unspecified-bool-type et remplit dont le critère pour paraître dans une assertion statique.

En outre, en C++11, l'expression :

static_assert(sizeof(long) > sizeof(int), "La bibliothèque doit être compilée sous un système 64-BIT");

permettrait à une bibliothèque d'être certaine qu'elle est compilée sur un système vérifiant cette condition (x86-64 par exemple).

Inférence de types [13]

Le mot clé auto se voit assigner une nouvelle sémantique par le nouveau standard. Nous connaissions son unique sémantique d'indicateur de classe de stockage pour une variable. En effet, déclarer une variable automatique revenait à indiquer au compilateur qu'elle était valide seulement dans l'espace où elle était déclarée ; ce comportement étant aussi celui par défaut, le mot clé était inutile. Dans le nouveau standard, il change de sémantique et prend la place du type dans la déclaration. Le type sera alors automatiquement décidé par correspondance avec le type retourné par l'objet utilisé pour l'initialisation de la variable. Les variables étant déclarées avec auto devront donc impérativement être initialisées. Exemple :

auto f = boost::bind(MyFunc, _1);
f(5);

Le type de f est un type interne de la bibliothèque surchargé environ quatre-vingts fois avec un script Perl. Trouver le type exact pour stocker le résultat d'un bind dans un objet n'était pas pratique du tout avant le nouveau rôle du mot clé auto, d'où son apparition.

D'une manière générale, l'utilisation du mot clé auto permet de passer moins de temps à écrire ce que le compilateur sait déjà.

Le nouveau standard ajoute le mot clé decltype qui permet de typer une variable à partir du type d'une autre variable. Exemple:

int i;
decltype(i) j = 5;

Le type de j sera du même type que i, soit int. Cette déclaration automatique du type d'une variable peut être très utile dans les templates.

Expressions et fonctions lambda [14],[15],[16]

Une fonction lambda construit une fermeture; c.-à-d. un objet fonction anonyme capable de capturer des variables dans la portée, de prendre des paramètres en entrée et de retourner un résultat. La syntaxe générale et complète est :

[ capture ] ( params ) mutable exception attribute -> ret { body }

. En particulier :

  • si aucune variable n'est à capturer, on écrira [];
  • si aucun paramètre n'est à passer, on écrira ();
  • si aucun retour n'est effectué ou si le compilateur peut déterminer le type de retour, on n'écrira pas la partie -> ret;
  • enfin, les parties mutable, exception et attribute sont optionnelles.

Exemple qui réalise l'extraction d'un nom de fichier à l'image de la commande basename :

auto basename([] (const std::string &str) {  // Spécification du retour explicite '-> const char *' inutile
    size_t pos = str.find_last_of("/\\");    // Séparateurs pour Linux et Windows
    const char *start = str.c_str();
    return pos != std::string::npos ? start + pos + 1 : start;
});

std::cout << "[" << str << "] -> [" << basename(str) << "]\n";  // Utilisation

Sémantique des RValues Reference/Move

L'introduction de la sémantique move (déplacement) prend son sens en constatant qu'en C++, il n'y a aucune manière générique de déplacer un objet sans le copier. Par exemple lorsqu'une fonction retourne un objet de grosse taille, celui-ci est copié dans une zone temporaire avant d'être à nouveau copié là où le résultat de la fonction est affecté. Après chaque étape de copie l'objet copié devient inutile et est détruit. Cela est très peu efficace car il serait beaucoup plus rapide de déplacer l'objet plutôt que de le recopier et détruire l'original. C'est particulièrement vrai si l'objet est d'un type proche du type T ci-dessous, où LargeDataT est un type d'objet coûteux à dupliquer :

class T
{
    LargeDataT *ptr;
public:
    T(const T &x) : ptr ( new LargeDataT (*x.ptr) ) {}
    ~T() { delete ptr; }
    void MoveFrom (T& x) { ptr = x.ptr; x.ptr = nullptr; }
};

En effet le déplacement d'un objet de ce type T requiert simplement la recopie du membre ptr alors que sa duplication alloue et copie un nouvel objet LargeDataT. Le problème que résout C++11 par l'ajout des RValues reference est de pouvoir appeler la fonction MoveFrom en lieu et place du constructeur de recopie dans les cas où la copie correspond à un déplacement. Ceci s'obtient par l'ajout du constructeur de déplacement ci-dessous :

    T(T &&x) : ptr (x.ptr) { x.ptr = nullptr; }

Le double & marque la référence sur rvalue (parfois aussi appelée temporaire). C'est-à-dire une référence sur quelque chose qui est temporaire ou est sur le point d'être détruit. Le constructeur de déplacement sera donc choisi par le compilateur à la place du constructeur de recopie en cas de copie d'un objet temporaire ou sur le point d'être supprimé. Sur tous les autres aspects une référence sur une rvalue est identique à une référence classique maintenant appelée référence sur lvalue (que l'on peut définir grossièrement par : tout ce qui a une adresse). De cette définition ressort un fait qui peut sembler paradoxal : une variable de type référence sur une rvalue n'est généralement pas une référence sur une rvalue ! En effet à partir du moment où une référence sur une rvalue est écrite dans une variable, y compris si elle est de type référence sur rvalue, elle perd son caractère temporaire dans l'espace de définition de cette variable.

Mais parfois il est utile d'appeler le constructeur de déplacement même à partir d'une variable qui n'est pas temporaire. Par exemple la commande swap est souvent introduite par le patron de fonction ci-dessous :

template <class T>
void swap ( T& a, T& b )
{
  T c(a); 
  a=b; 
  b=c;
}

Cette fonction a pour inconvénient d'appeler d'abord le constructeur de recopie, puis deux opérateurs d'assignation. Ce sont donc 3 copies au total, qui peuvent être des opérations extrêmement coûteuses, voire impossibles si les objets impliqués sont de taille importante. Ici le constructeur de déplacement n'est pas appelé car a, b et c comme source de la copie ne sont pas temporaires. C++11 introduit la fonction std::move() qui retourne une référence à une rvalue et prend pour paramètre une référence à une lvalue ou à une rvalue. Son patron est le suivant :

template <class T>
typename remove_reference<T>::type&&
move(T&& a)
{
    return a;
}

La fonction move() donne à ce qu'il retourne la valeur de son paramètre. La fonction move ne modifie pas l'objet qui lui est passé mais reçoit et fournit une référence sur un objet non constant. L'objet d'origine peut donc être modifié à partir du résultat de la fonction move(). Le point important de move est qu'il n'y a aucune copie de faite. En utilisant move, on peut ainsi réécrire de façon concise swap(), sans qu'il n'y ait de copie.

template <class T> 
void swap(T& a, T& b)
{
    T tmp(std::move(a));
    a = std::move(b);   
    b = std::move(tmp);
}

Un autre intérêt de move() est de permettre d'obtenir une copie d'un objet volumineux sans qu'il y ait de copie réelle de celui-ci, en particulier de la partie volumineuse.

De plus, move() est impératif dans le cas d'un objet non copiable comme le “smart pointer” unique_ptr.

Énumérations fortement typées [17]

L'énumération du langage C était similaire à une liste de définitions de symboles (macros) correspondant à des nombres entiers, et C++ n'avait répondu qu'en interdisant la conversion d'un type énumération dans un autre.

C++11 proposera des énumérations « fortement typées ». Ces énumérations seront obtenues en remplaçant enum par enum class ou enum struct. La conversion implicite d'éléments de ces énumérations vers les entiers sera prohibée et l'accès aux éléments se fera à l'aide de l'opérateur de résolution de portée. Voici un exemple d'utilisation :

enum class Chiffres { Zero, Un, Deux, Trois, Quatre, Cinq, Six, Sept, Huit, Neuf };
Chiffres chif;
chif = Chiffres::Sept;
int nb;
nb = (int) Chiffres::Trois;

L'opérateur de résolution de portée sera optionnel avec des énumérations faiblement typées :

enum Enum2 { E_1, E_2 };
Enum2 e1 = E_1; // Comme en C++03
Enum2 e2 = Enum2::E_2; // Comme avec une enum class;

De plus, C++11 vous permettra de choisir le type d'entier sous-jacent des énumérations (tous sauf wchar_t):

enum class Enumeration : unsigned short { Valeur1, Valeur2 };

Par défaut, ce type sera int. Ce comportement sera aussi possible avec les énumérations normalement typées, et il sera bien sûr toujours possible de définir la valeur d'une partie de l'énumération :

enum EnumerationNormale : UINT8 { ValeurDepart = 0, ValeurMoyenne = 127, ValeurMaximum = 255 };

Boucles basées sur des intervalles [18]

Le code nécessaire en C++ pour le parcours d'un intervalle et l'action sur ses éléments est répétitif et long. De nombreux langages, comme Java, ont fourni à leurs utilisateurs un opérateur foreach qui permet de parcourir une liste avec aisance[note 1]. Pour répondre aux attentes, la norme C++11 fournira une nouvelle syntaxe de l'instruction for qui s'implémentera de cette façon :

int mon_tableau[5] = {1, 2, 3, 4, 5};
for (int &x: mon_tableau) {
    x *= 2;
}

Ce code permet de doubler tous les éléments du tableau mon_tableau. L'entier x défini pour le corps de la boucle for référence successivement chacun des éléments du tableau.

Ce type de parcours fonctionnera pour les listes classiques, les listes d'initialiseurs, ainsi que les conteneurs de la STL définissant les fonctions membres begin et end comme dans l'exemple suivant :

std::vector<int> myvector;

myvector.push_back(100);
myvector.push_back(200);
myvector.push_back(300);

// Show content
std::cout << "\nShow content of " << myvector.size() << " elements\n";

std::cout << "Version with iterator\n";
for (std::vector<int>::iterator it = myvector.begin(); it != myvector.end(); ++it) {
    std::cout << *it << '\n';
}

std::cout << "Version with [] operator\n";
for (size_t n = 0; n < myvector.size(); ++n) {
    std::cout << myvector[n] << '\n';
}

std::cout << "Version 'foreach'\n";
for (int value: myvector) {      // for (auto value: myvector) est recommandé car aucune ambiguïté 
    std::cout << value << '\n';
}

Littéraux définis par l'utilisateur [19]

Les littéraux définis par l'utilisateur[note 2] permettent d'ajouter de nouveaux suffixes, au même titre que UL, ULL, L... Les suffixes créés ainsi doivent commencer par un tiret bas (_) afin de ne pas entrer en conflit avec les futurs littéraux introduits dans la librairie standard.

Exemple :

struct Distance
{
    int d; // d in mm
    Distance(int mm):
        d(mm);
    {}
}

inline constexpr Distance operator ""_mm(unsigned long long value)
{
    return Distance(value);
}

inline constexpr Distance operator ""_m(unsigned long long value)
{
    return Distance(value * 1000);
}

inline constexpr Distance operator ""_cm(unsigned long long value)
{
    return Distance(value * 10);
}

auto DistanceChaiseBureau = 20_cm; // Instance de Distance, d = 200
auto DistanceMachineACafe = 5_m;   // Instance de Distance, d = 5000

Pointeur NULL [20]

Le nouveau mot-clé nullptr a été proposé comme constante du langage avec le caractère particulier d'être assignable à tous les types de pointeurs. En effet, contrairement au C où la macro préprocesseur est généralement définie avec #define NULL ((void*)0)​, en C++ il est interdit d'assigner un void* à un pointeur d'un type différent. L'usage était donc de définir NULL avec l'entier 0. Ce comportement restera compatible, mais il sera aussi possible d'écrire :

T* ptr = nullptr;

La constante NULL définie comme l'entier 0 ne permettait pas au compilateur de déterminer quelle surcharge de f choisir dans le code suivant:

void f(int);
void f(void*);

f(0); // Entier 0 ou pointeur nul?

Le mot clé nullptr est une constante du type nullptr_t, non convertible en entier. Pour appeler la fonction f avec un pointeur NULL, la surcharge est correctement choisie en C++11 dans le code suivant:

void f(int);
void f(void*);

f(0); // Entier 0, pas d'ambiguïté
f(nullptr); // Convertible en void*, mais pas en int.

Extension de la bibliothèque standard

Threads

La bibliothèque standard a implémenté dans la nouvelle norme du C++, le modèle de classe std::thread, celui-ci n'est qu'une implémentation des threads de la bibliothèque Boost.

Voici, un exemple résumant quelque peu son utilisation:

void thread () { std::cout << "WikiFunction" << std::endl; }

int main ()
{
   std::thread wikifunction(thread); // Création et lancement du thread

   wikifunction.join(); // Attend la fin du thread
}

Type tuple

Un tuple est une collection de dimension fixe d'objets de types différents. Tout type d'objet peut être élément d'un tuple. Cette nouvelle fonctionnalité est implémentée dans un nouvel en-tête et bénéficie des extensions de C++11 comme :

Le patron de classe tuple est déclaré par la ligne :

template <class... Types> class tuple;

Un exemple de définition et d'utilisation du type tuple :

typedef tuple< int, double, long &, const char * > test_tuple ;
long lengthy = 12 ;
test_tuple proof( 18, 6.5, lengthy, "Ciao!" ) ;
lengthy = get<0>(proof) ;  // Assigne à ‘lengthy’ la valeur 18
get<3>(proof) = " Beautiful!" ;  // Modifie la {{4e}} valeur du tuple

Il est possible de créer le tuple proof sans définir son contenu si les éléments du tuple possèdent un constructeur par défaut. De plus, il est possible d'assigner un tuple à un autre tuple : si les deux tuples sont de même type, il est nécessaire que chaque élément du tuple ait un constructeur par copie, sinon il faut que le type de chaque élément de l'opérande de droite soit compatible avec le type correspondant dans l'opérande de gauche ou que l'élément correspondant de l'opérande gauche ait un constructeur approprié.

typedef tuple< int , double, string       > tuple_1 t1 ;
typedef tuple< char, short , const char * > tuple_2 t2( 'X', 2, "Hola!" ) ;
t1 = t2 ;  // OK : les deux premiers éléments peuvent être convertis,
           // le troisième peut être construit à partir du ‘const char *’.

Les opérateurs relationnels sont disponibles (pour les tuples ayant le même nombre d'éléments). Deux expressions sont introduites pour vérifier les caractéristiques d'un tuple (à la compilation) :

  • tuple_size<T>::value< retourne le nombre d'éléments du tuple T,
  • tuple_element<I, T>::type retourne le type de l'objet placé en position I du tuple T.

Table de hachage

Intégrer les tables de hachage (conteneurs associatifs non ordonnés) dans la bibliothèque standard du C++ est l'une des demandes les plus récurrentes. Cela n'avait pas été réalisé pour la norme actuelle (celle écrite en 1995 et approuvée en 1998) à cause des contraintes de temps. Bien que cette solution soit moins efficace que les arbres équilibrés dans le pire des cas (en cas de collisions importantes), elle est cependant la meilleure dans la plupart des applications réelles.

Les collisions seront uniquement gérées par chaînage linéaire car le comité ne considère pas opportun de standardiser des solutions d'adressage ouvert qui introduisent un nombre important de problèmes intrinsèques (en particulier quand la suppression d'éléments est permise). Pour éviter les conflits de noms avec les bibliothèques non standards qui ont leur propre implémentation des tables de hachage, on utilisera le préfixe unordered, au lieu de hash.

Cette nouvelle fonctionnalité intégrera quatre types de tables de hachage, différentes selon qu'elles acceptent ou non des éléments de même clé (clé unique ou clé équivalente) et qu'elles associent chaque clé à la valeur associée.

Type de table de hachageType associé arbitraireClés équivalentes
unordered_set
unordered_multiset
unordered_map
unordered_multimap

Ces nouvelles classes remplissent toutes les demandes des classes de conteneurs et contiennent toutes les méthodes nécessaires pour accéder aux éléments : insert, erase, begin, end.

Ces classes n'ont pas nécessité les nouvelles extensions du langage C++ mais seulement une légère extension du header <functional> et l'introduction des headers <unordered_set> et <unordered_map>. Aucun autre changement aux classes de la norme actuelle n'est nécessaire et elles ne dépendent d'aucune autre extension de la bibliothèque standard.

Expressions rationnelles

La bibliothèque définie dans le fichier d'en-tête <regex> est fait d'un ensemble de nouvelles classes :

  • Les expressions rationnelles sont représentées par une instance de la classe template std::regex
  • Les résultats sont représentés par une instance de la classe template std::match_results

La fonction std::regex_search est utilisée pour une recherche. std::regex_replace est utilisée pour effectuer un "chercher-remplacer", elle renvoie pour cela une nouvelle chaîne.

Les algorithmes std::regex_search et std::regex_replace prennent une expression rationnelle et une chaîne et écrivent les occurrences trouvées dans la structure std::match_results.

Voici, un exemple d'utilisation de std::match_results :

const char *reg_esp = "[ ,.\\t\\n;:]";  // Une liste de caractères séparateurs

// On pourrait aussi utiliser les chaînes littérales
// const char *reg_esp = R"([ ,.\t\n;:])";

std::regex rgx(reg_esp);  // 'regex' est une instance de la classe
                      // 'basic_regex' avec un argument de type 'char'.
std::cmatch match;  // 'cmatch' est une instance de la classe
                // 'match_results' avec un argument de type 'const char *'.
const char *target = "Unseen University - Ankh-Morpork";

// Trouve tous les mots de 'target' séparés par les caractères de 'reg_esp'.
if( std::regex_search( target, match, rgx ) ) {
    const size_t n = match.size();
    for( size_t a = 0; a < n; a++ ) {
        std::string str( match[a].first, match[a].second );
        std::cout << str << "\n";
    }
}

Notez l'utilisation du double backslash, car le C++ utilise le backslash comme un caractère d'échappement. Les chaînes littérales en C++11 peuvent permettre d'éviter le problème.

L'utilisation de la bibliothèque <regex> ne requiert aucune dépendance explicite.

Amélioration des nombres aléatoires extensibles

La bibliothèque standard du C permet de générer des nombres pseudo-aléatoires grâce à la fonction rand. L'algorithme de génération n'est pas standardisé mais laissé au choix du fournisseur de la bibliothèque. Le C++ n'y a rien changé, mais C++11 va fournir une manière différente de générer les nombres pseudo-aléatoires.

Cette fonctionnalité est découpée en deux parties qui forment un objet de génération de nombres aléatoires :

  • un moteur de génération, qui contient l'état du générateur et produit les nombres pseudo-aléatoires ;
  • une distribution, qui détermine les valeurs que le résultat peut prendre ainsi que sa loi de probabilité.

C++11 définit trois algorithmes de génération, chacun ayant des avantages et des inconvénients.

TemplateEntier/flottantQualitéVitesseTaille d'état
linear_congruential EntierMoyenneMoyenne1
subtract with carry Les deuxMoyenneRapide25
mersenne_twister EntierBonneRapide624

C++11 fournira un certain nombre de lois standard : uniform_int_distribution, bernoulli_distribution, geometric_distribution, poisson_distribution, binomial_distribution, uniform_real_distribution, exponential_distribution, normal_distribution et gamma_distribution.

Le générateur et la distribution se combinent comme dans l'exemple suivant :

std::uniform_int_distribution<int> distribution(0, 99);
std::mt19937 engine;
auto generator = std::bind(distribution, engine);
int random = generator();  // Generate a uniform integral variate between 0 and 99.

Fonctions mathématiques spéciales

Le fichier header <math> définit déjà plusieurs fonctions mathématiques usuelles :

  • trigonométriques : sin, cos, tan, asin, acos, atan, atan2;
  • hyperboliques : sinh, cosh, tanh, asinh, acosh, atanh;
  • exponentielles : exp, exp2, frexp, ldexp, expm1;
  • logarithmiques : log10, log2, logb, ilogb, log1p;
  • puissances : pow, sqrt, cbrt, hypot;
  • spéciales : erf, erfc, tgamma, lgamma.

Le comité a décidé d'ajouter de nouvelles fonctions qui nécessitent actuellement l'utilisation de bibliothèques non-standards. Ces nouvelles fonctions auront un intérêt principalement pour les programmeurs de disciplines scientifiques et pour l'ingénierie.

Le tableau suivant montre les 23 fonctions décrites dans TR1.

Nom de la fonctionPrototype de la fonctionExpression mathématique
Polynômes de Laguerre généralisés double assoc_laguerre(unsigned n, unsigned m, double x);
Polynômes de Legendre généralisés double assoc_legendre(unsigned l, unsigned m, double x);
Fonction bêta double beta(double x, double y);
Intégrale elliptique complète de premier genre double comp_ellint_1(double k);
Intégrale elliptique complète de deuxième genre double comp_ellint_2(double k);
Intégrale elliptique complète de troisième genre double comp_ellint_3(double k, double nu);
Fonctions hypergéométriques confluentes double conf_hyperg(double a, double c, double x);
Fonctions de Bessel cylindriques modifiées régulières double cyl_bessel_i(double nu, double x);
Fonctions de Bessel cylindriques du premier genre double cyl_bessel_j(double nu, double x);
Fonctions de Bessel cylindriques modifiées irrégulières double cyl_bessel_k(double nu, double x);
Fonctions de Neumann cylindriques

Fonctions de Bessel cylindriques du deuxième genre

double cyl_neumann(double nu, double x);
Intégrale elliptique incomplète du premier genre double ellint_1(double k, double phi);
Intégrale elliptique incomplète du deuxième genre double ellint_2(double k, double phi);
Intégrale elliptique incomplète du troisième genre double ellint_3(double k, double nu, double phi);
Intégrale exponentielle double expint(double x);
Polynômes d'Hermite double hermite(unsigned n, double x);
Séries hypergéométriques double hyperg(double a, double b, double c, double x);
Polynômes de Laguerre double laguerre(unsigned n, double x);
Polynômes de Legendre double legendre(unsigned l, double x);
Fonction zêta de Riemann double riemann_zeta(double x);
Fonctions sphériques de Bessel du premier genre double sph_bessel(unsigned n, double x);
Fonctions sphériques de Legendre généralisées double sph_legendre(unsigned l, unsigned m, double theta);
Fonctions sphériques de Neumann

Fonctions sphériques de Bessel du deuxième genre

double sph_neumann(unsigned n, double x);

Chacune de ces fonctions possède deux variantes supplémentaires. En rajoutant le suffixe f ou l au nom de la fonction, on obtient les mêmes fonctions agissant sur des float ou des long double respectivement. Par exemple :

float sph_neumannf( unsigned n, float x ) ;
long double sph_neumannl( unsigned n, long double x ) ;

Notes et références

Notes

  1. L'algorithme std::for_each du C++ ne sert qu'à appliquer une fonction à tous les éléments d'une séquence de conteneur (voir la documentation de cette fonction).
  2. Explication détaillée et exemples en 3 parties : (en) « Partie 1 » - (en) « Partie 2 » - (en) « Partie 3 »

Références

  1. Blog d'Herb Sutter : March 2010 ISO C++ Standards Meeting
  2. Blog d'Herb Sutter : We have an international standard: C++0x is unanimously approved
  3. (en) « ISO/CEI 14882:2011 », ISO
  4. (en) Herb Sutter et Francis Glassborow, « N1986 - Delegating Constructors » [PDF], 2006‐04‐06
  5. (en) Alisdair Meredith, Michael Wong et Jens Maurer, « N2540 - Inheriting Constructors »,
  6. (en) Jason Merrill et Daveed Vandevoorde, « N2672 - Initializer List proposed wording »,
  7. (en) Douglas Gregor, Jaakko Järvi, Jens Maurer et Jason Merrill, « N2242 - Proposed Wording for Variadic Templates » [PDF],
  8. (en) Douglas Gregor et Eric Niebler, « N2555 - Extending Variadic Template Template Parameters » [PDF],
  9. (en) « Status of Experimental C++0x Support in GCC 4.3 – GNU Project – Free Software Foundation (FSF) »
  10. (en) Daveed Vandevoorde, « N1757 - Right Angle Brackets »,
  11. (en) John Spicer, « N1987 - Adding "extern template" »,
  12. (en) Robert Klarer, Dr. John Maddock, Beman Dawes et Howard Hinnant, « N1720 - Proposal to Add Static Assertions to the Core Language »,
  13. (en) Jaakko Järvi, Bjarne Stroustrup et Gabriel Dos Reis, « N1984 - Deducing the type of variable from its initializer expression » [PDF],
  14. (en) Jaakko Järvi, John Freeman et Lawrence Crowl, « N2550 - Lambda Expressions and Closures: Wording for Monomorphic Lambdas » [PDF],
  15. (en) Jaakko Järvi, Peter Dimov et John Freeman, « N2658 - Constness of Lambda Functions » [PDF],
  16. (en) Daveed Vandevoorde, « N2927 - New wording for C++0x Lambdas » [PDF],
  17. (en) Herb Sutter et David E. Miller, « N1719 - Strongly Typed Enums » [PDF],
  18. (en) Douglas Gregor et Beman Dawes, « N2930 - Range-Based For Loop Wording »,
  19. (en) Ian McIntosh, Michael Wong, Raymond Mak et al., « N2765 - User-defined Literals » [PDF],
  20. (en) Herb Sutter et Bjarne Stroustrup, « N2431 - A name for the null pointer: nullptr » [PDF],

Voir aussi

Articles connexes

Liens externes

  • Portail de la programmation informatique
Cet article est issu de Wikipedia. Le texte est sous licence Creative Commons - Attribution - Partage dans les Mêmes. Des conditions supplémentaires peuvent s'appliquer aux fichiers multimédias.