AccueilđŸ‡«đŸ‡·Chercher

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

Cet article est issu de wikipedia. Text licence: CC BY-SA 4.0, Des conditions supplĂ©mentaires peuvent s’appliquer aux fichiers multimĂ©dias.