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 templateclass ... 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]
<>â
) [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â
etattributeâ
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]
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 :
- Les templates variadiques
- Référence sur référence
- Arguments par défaut pour les fonctions template
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 tupleTâ
,tuple_element<I, T>::typeâ
retourne le type de l'objet placĂ© en positionIâ
du tupleTâ
.
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 hachage | Type associé arbitraire | Clé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.
Template | Entier/flottant | Qualité | Vitesse | Taille d'état |
---|---|---|---|---|
linear_congruentialâ |
Entier | Moyenne | Moyenne | 1 |
subtract with carryâ |
Les deux | Moyenne | Rapide | 25 |
mersenne_twisterâ |
Entier | Bonne | Rapide | 624 |
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 fonction | Prototype de la fonction | Expression 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
- 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). - Explication détaillée et exemples en 3 parties : (en) « Partie 1 » - (en) « Partie 2 » - (en) « Partie 3 »
Références
- Blog d'Herb Sutter : March 2010 ISO C++ Standards Meeting
- Blog d'Herb Sutter : We have an international standard: C++0x is unanimously approved
- (en) « ISO/CEI 14882:2011 », ISO
- (en) Herb Sutter et Francis Glassborow, « N1986 - Delegating Constructors » [PDF], 2006â04â06
- (en) Alisdair Meredith, Michael Wong et Jens Maurer, « N2540 - Inheriting Constructors »,
- (en) Jason Merrill et Daveed Vandevoorde, « N2672 - Initializer List proposed wording »,
- (en) Douglas Gregor, Jaakko JÀrvi, Jens Maurer et Jason Merrill, « N2242 - Proposed Wording for Variadic Templates » [PDF],
- (en) Douglas Gregor et Eric Niebler, « N2555 - Extending Variadic Template Template Parameters » [PDF],
- (en) « Status of Experimental C++0x Support in GCC 4.3 â GNU Project â Free Software Foundation (FSF) »
- (en) Daveed Vandevoorde, « N1757 - Right Angle Brackets »,
- (en) John Spicer, « N1987 - Adding "extern template" »,
- (en) Robert Klarer, Dr. John Maddock, Beman Dawes et Howard Hinnant, « N1720 - Proposal to Add Static Assertions to the Core Language »,
- (en) Jaakko JÀrvi, Bjarne Stroustrup et Gabriel Dos Reis, « N1984 - Deducing the type of variable from its initializer expression » [PDF],
- (en) Jaakko JÀrvi, John Freeman et Lawrence Crowl, « N2550 - Lambda Expressions and Closures: Wording for Monomorphic Lambdas » [PDF],
- (en) Jaakko JÀrvi, Peter Dimov et John Freeman, « N2658 - Constness of Lambda Functions » [PDF],
- (en) Daveed Vandevoorde, « N2927 - New wording for C++0x Lambdas » [PDF],
- (en) Herb Sutter et David E. Miller, « N1719 - Strongly Typed Enums » [PDF],
- (en) Douglas Gregor et Beman Dawes, « N2930 - Range-Based For Loop Wording »,
- (en) Ian McIntosh, Michael Wong, Raymond Mak et al., « N2765 - User-defined Literals » [PDF],
- (en) Herb Sutter et Bjarne Stroustrup, « N2431 - A name for the null pointer: nullptr » [PDF],