La réunion d'automne du comité WG21 sur l'ajout de fonctionnalités au C ++ 20 est achevée,
Voici quelques nouveautés introduites au langage

Le , par Bill Fassinou, Chroniqueur Actualités
Pour rappel, le comité ISO C ++, encore appelé Working Group 21 (WG21), est un comité de standardisation du langage C++ qui comprend des experts accrédités des pays membres de l’ISO intéressés par le langage C ++. Entre autre ces pays, on peut citer le Canada, la Finlande, la France, l'Allemagne, les Pays-Bas, la Russie, l'Espagne, la Suisse, le Royaume-Uni et les États-Unis. Le comité organise trois fois par an des réunions en face-à-face d'une semaine pendant lesquelles, les experts conviés discutent des sujets techniques concernant le langage. L'organisateur du comité est Herb Sutter, un programmeur et un consultant américain, expert reconnu du langage C++ et auteur de plusieurs ouvrages sur ce sujet.

La dernière réunion du comité date du samedi 10 novembre dernier où le comité ISO C ++ a achevé sa réunion d'automne à San Diego en Californie. « C’était la plus grande réunion ISO C ++ de nos 29 ans d’existence, avec environ 180 participants, représentant 12 pays », précise Sutter dans son billet de blog. « Comme il s’agit d’une des dernières réunions sur l’ajout de fonctionnalités au C ++ 20, nous avons donné la priorité aux propositions susceptibles de faire du C ++ 20 et nous en avons adopté un certain nombre », a-t-il continué en remerciant tous les participants à ce comité dont l'objectif est de poursuivre les travaux sur la prochaine norme internationale du langage, le C ++ 20. Cette réunion était la dernière réunion à examiner de nouvelles propositions pour C ++ 20.


Le comité est organisé en plusieurs groupes et sous-groupes, chacun dirigé par le président désigné. La dernière édition du comité a vu grandir le nombre de ces participants. « La réunion de cette semaine a été extraordinaire. L'envoi de la réunion préparatoire comprenait 274 communications, soit environ deux fois plus que toute réunion précédente, et 180 personnes y ont assisté, ce qui en fait la plus grande réunion de comité C ++ jamais organisée », peut-on lire dans le compte rendu de la réunion. La première étape a été la formation de deux nouveaux groupes d’étude à savoir le SG17, incubateur du groupe de travail sur l'évolution du EWGI, présidé par JF Bastien et le SG18, incubateur du groupe de travail sur l'évolution des bibliothèques LEWGI, présidé par Bryce Adelstein Lelbach.

Ces nouveaux groupes d’incubateurs devraient constituer des étapes supplémentaires dans les filières d’évolution des langues et des bibliothèques. Leur travail consiste à affiner, fusionner et filtrer les propositions nouvelles et tournées vers l’avenir pour permettre la réalisation des objectifs stratégiques du comité à long terme à savoir, améliorer la prise en charge de logiciels fiables à grande échelle et des modèles de concurrence, simplifier l'utilisation du langage, corriger les principales sources d'insatisfaction et les principales sources d'erreur du langage, etc. Deux autre nouveaux groupes ont été également créés lors de cette réunion : le SG19, pour l'apprentissage automatique présidé par Michael Wong et le SG20, pour l'éducation présidé par JC van Winkel.

Lors de cette réunion, deux fonctionnalités principales ont été adoptées pour le C ++ 20 au niveau de la librairie Ranges et des notations de commodité. La notation de commodité a déjà été ajoutée à la fonctionnalité de base des concepts de C ++ 20 en 2017, mais sans la notation pour écrire des modèles sans les mots-clés "template" ou "require". « Lors de cette réunion, nous avons finalement convergé sur une syntaxe de commodité pour écrire des modèles contraints abordant tous les problèmes majeurs identifiés par les concepteurs de la notation de commodité de Concepts TS, tout en étant acceptables pour les concepteurs de concepts principaux. Pour la première fois, outre le cas particulier des lambdas génériques, C ++ vous permettra désormais d'écrire de nombreuses fonctions génériques sans “template” ni crochets angulaires », explique Herb Sutter dans son billet de blog.


D'autres nouveautés et améliorations ont été également apportées à C ++ 20 lors de cette réunion. Entre autre, on a :

  • les fonctions immédiates « consteval » qui permettent d'écrire des fonctions dont l'exécution est garantie au moment de la compilation. « C’est essentiel pour le type de code sur lequel les métaclasses s’appuient, et c’est formidable de le voir maintenant adopté dans le cadre de C ++ 20 », commente le Sutter ;
  • l'union constexpr qui permet de changer le membre actif d'une union pendant le code au moment de la compilation ;
  • constexpr try and catch qui permet d'écrire try / catch dans le code constexpr. « Cependant, notez que cela ne vous permet pas de générer des exceptions lors de la compilation », précise le Sutter ;
  • la révision du modèle de mémoire C ++ qui apporte plusieurs mises à jour pour les architectures plus faibles, notamment une meilleure prise en charge de la mémoire GPU ;
  • le contrôle d'accès dans les conditions du contrat qui permet aux conditions préalables et post-conditions des fonctions publiques d'accéder également à des membres privés ;
  • char8_t : un type pour les caractères et les chaînes UTF-8 qui est une autre étape vers la prise en charge étendue de Unicode ;
  • les espaces de noms en ligne imbriqués qui étendent la nouvelle syntaxe de déclaration de l'espace de noms imbriquée C ++ 20, également pour les espaces de nom en ligne ;
  • etc.

« Nous sommes sur la bonne voie pour rendre la plupart des codes C ++ "normaux" et disponibles au moment de la compilation - et bien que le C ++ 20 n’y parvienne pas complètement, le C ++ 20 est une version historique et un point de départ décisif pour laisser en permanence les crochets et solutions palliatives que nous utilisons depuis les années 1990, avec la naissance presque complète du code C ++ de compilation entièrement "naturel". Rappelons que nous avons déjà ajouté la prise en charge des types définis par l'utilisateur en tant que types de paramètres de modèle. Bientôt, nous pourrons peut-être utiliser des chaînes comme arguments de modèle et utiliser des conteneurs tels que std::vector dans le code à la compilation. Bien sûr, il y aura probablement des limites. Par exemple, supporter std::thread au moment de la compilation est possible, mais il est moins probable que l'effort en vaille la peine », conclut Herb Sutter, organisateur de la réunion.

Les internautes se sont prononcés sur le travail de ce comité. Ils les ont félicité pour l'effort fourni et n'ont pas manqué l'occasion d'exprimer les fonctionnalités qu'ils auraient souhaité avoir dans le C ++ 20. L'un d'eux dit par exemple que la seule fonctionnalité dont il a besoin au moment de la compilation est que le compilateur calcule lui-même les déclarations. Il ajoute qu'en 2018, la gestion manuelle des déclarations et des fichiers d'en-tête n'est plus nécessaire.

Source : Billet de blog

Et vous ?

Que pensez-vous de ces améliorations apportées au C ++ 20 ?
Quelles autres améliorations aimeriez-vous avoir dans le langage ?

Voir aussi

La spécification de la nouvelle version du langage C++ (C++17) est finalisée quelles sont les nouveautés introduites ?

Cours de C/C++

De C++14 à C++17, qu'est-ce qui a changé avec la nouvelle version du langage C++ : un document de la Standard C++ Foundation

Visual Studio "15" : de nouvelles fonctionnalités C++14/17 et des améliorations pour les développeurs Python dans la cinquième préversion


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :


 Poster une réponse Signaler un problème

Avatar de onilink_ onilink_ - Membre éprouvé https://www.developpez.com
le 18/11/2018 à 23:19
Je pense que C++20 sera une évolution majeure avec autant d'impact que C++11.
J'ai hâte de pouvoir en faire, même si malheureusement ça va demander beaucoup de temps pour un support général...

En tout cas le C++ continue dans la bonne direction et cela fait plaisir.
Avatar de Matthieu76 Matthieu76 - Membre éclairé https://www.developpez.com
le 19/11/2018 à 10:43
Oui, que des bonnes nouvelles !
Avatar de Matthieu76 Matthieu76 - Membre éclairé https://www.developpez.com
le 21/11/2018 à 13:54
Citation Envoyé par onilink_ Voir le message
Je pense que C++20 sera une évolution majeure avec autant d'impact que C++11.
J'ai lu un peu plus en détail et je vois pas trop ce qui est nouveau

  1. Les contrats je ne connaissais pas et ça a l'air génial mais ça existe déjà depuis C++11, juste là ce sera un peu plus formel.
  2. Les concepts, ça a pas trop l'air très utile. (Après je ne suis pas expert, j'ai surement tort...).
  3. Les coroutines je vois pas ce que c'est même si j'ai l'impression que ça ce rapproche beaucoup des QFutures avec la lib Qt ou des Task en C#.


J'ai l'impression que c'est plus de la simplification d'écriture et de petits trucs par ci par là plutôt que de réel nouveauté.

PS : Ce n'est que me avis et j'ai surement tors, je ne suis pas assez expert en C++ pour voir la réelle utilité de tout cela. Faudrait que je me renseigne plus.
Avatar de jo_link_noir jo_link_noir - Membre émérite https://www.developpez.com
le 22/11/2018 à 1:34
Citation Envoyé par Matthieu76 Voir le message
Les contrats je ne connaissais pas et ça a l'air génial mais ça existe déjà depuis C++11, juste là ce sera un peu plus formel.
Les contrats dans la plupart des langages sont implicites et disponible via les commentaires ou à travers l'implémentation. Il n'y a pas moyen de dire dans la déclaration même de la fonction qu'elle accepte par exemple seulement les nombres comprit entre 0 et 9. Les contrats permettent cela et le compilateur pourra le vérifier. Les outils d'analyse statique pourront se baser dessus pour fournir un meilleur diagnostique et le compilateur pourra faire plus d'optimisation en ayant connaissance des post/pré-conditions.

Citation Envoyé par Matthieu76 Voir le message
Les concepts, ça a pas trop l'air très utile.
Au contraire, c'est quelque chose d’extrêmement puissant et on les utilise implicitement dans la stl ou explicitement à travers une interface. Dans le cas d'une interface, le comportement va changer, mais les membres sont toujours présents, notre interface est un concept. Dans le cas de la stl, chaque algorithme fonctionne avec un certain type d'itérateurs: FwIterator, RandomIterator, etc. Si une partit du concept n'est pas respecté, il y a de forte chance de se manger une erreur imbitable du compilateur du tréfonds de la SL. Les concepts permettront d'avoir des erreurs le plus tôt possible, c’est-à-dire au moment de l'appel d'une fonction.

Et surtout, ils vont très fortement réduire l'usage de template<class.......> ou de auto, car un type template et auto sont les concepts les plus générique qui puisse exister: ils acceptent tout. C'est la suite logique des guides de déduction.

Un exemple avec une implémentation possible de std::advance:

Avant les concepts:

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
template<class RandomIterator, class Distance>
std::enable_if_t<is_random_iterator_v<RandomIterator>> advance(RandomIterator& it, Distance n)
{
  it += n;
}
 
template<class InputIterator, class Distance>
std::enable_if_t<is_input_iterator_v<InputIterator> && !is_random_iterator_v<InputIterator>> advance(InputIterator& it, Distance n)
{
  while (n--) ++it;
}
Avec les concepts:

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
// RandomIterator est concept, il prend accepte n'importe quel type qui correspond aux prérequis demandés. advance est donc bien une fonction template
void advance(RandomIterator& it, Distance n)
{
  it += n;
}
 
// InputIterator est lui aussi un concept. Grâce au SFINAE, et du fait qu'il est plus restrictif que RandomIterator, le compilateur va se rabattre sur cette implémentation si la précédente n'est pas possible.
void advance(InputIterator& it, Distance n)
{
  while (n--) ++it;
}
Les coroutines je vois pas ce que c'est même si j'ai l'impression que ça se rapproche beaucoup des QFutures avec la lib Qt ou des Task en C#.
Pas vraiment, Si je ne dis pas de bêtise, QFuture est Task sont pour de la programmation concurrentielle: avoir plusieurs "fonctions" qui s’exécutent, mais pas forcement en même temps. Une fonction est mise en pause et une autre prend la relève lorsqu'il y a une attente (typiquement sur les IOs).

Le but d'une coroutine est de pouvoir remonter le flux d'appel pour revenir dessus sur demande. C'est quelque chose qu'on retrouve pas mal dans des langages de script avec le mot clef yield.

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function generator() {
  // des trucs
  // ...
  yield 1
  // encore des trucs
  // ...
  yield 2
  // d'autres trucs
  // ...
  yield 3
}
 
g = generator()
print(g()) // 1
print(g()) // 2
print(g()) // 3

Il y aussi les Ranges qui sont vraiment intéressants. Cela va énormément simplifier l'utilisation des algorithmes, car il n'y aura plus besoin de mettre systématiquement begin/end et une bonne partie des algorithmes peuvent être déplacés dans les sentinelles (un nouveau concept introduit par les ranges). Une sentinelle est un sous-concept d'itérateur, il n'y a que les fonctions de comparaisons (le range-for de c++17 le supporte déjà).

Avant les sentinelles:

Code : Sélectionner tout
1
2
char const* s = ....;
auto it = std::find_if(s, s+strlen(s), pred); // il nous faut forcement la position de fin
Avec les sentinelles:

Code : Sélectionner tout
1
2
char const* s = ....;
auto it = std::find_if(s, until('\0'), pred); // until retourne un une sentinelle qui fait `*it != '\0'` dans operator!=.
Avatar de Matthieu76 Matthieu76 - Membre éclairé https://www.developpez.com
le 22/11/2018 à 14:46
Oui je suis d'accord, les contrats ça à l'air vraiment bien, surtout si ça peut éviter d'avoir des try catch un peu partout car au final on ne se souvient jamais à quoi ils servent mais on les laisse par peur de pété un truc un jour et ça rends le code encore plus illisible. Et plus pour tout ce qui est calcul mathématique les contrats ont l'air vraiment bien adapter. Par exemple ma fonction y = sqrt(x) prends forcément un nombre positif en entrée, x > y pour tout x > 1, x < y pour tout x < 1. Ça, ça devient super simple et lisible à matérialiser avec des contrats.

Ok les concepts c'est mieux que les templates mais j'utilises jamais de temples, je trouve que ça complexifie le code pour rien, soit je fais une classe commune grace à l’héritage multiple soit je fais directement 2 méthodes.

auto, c'est juste pour pas l'embêter à écrire le nom de l' object, non ?
auto obj = new ObjectWithVeryLongClassName();InputIterator et RandomIterator je connais pas du tout.

Pas vraiment, Si je ne dis pas de bêtise, QFuture est Task sont pour de la programmation concurrentielle: avoir plusieurs "fonctions" qui s’exécutent, mais pas forcement en même temps. Une fonction est mise en pause et une autre prend la relève lorsqu'il y a une attente (typiquement sur les IOs).

Je n'utilise pas non plus les yield. Je ne trouve pas cela très utile et ça complexifie beaucoup le code pour pas grand-chose.

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function generator() {
  // des trucs
  // ...
  yield 1
  // encore des trucs
  // ...
  yield 2
  // d'autres trucs
  // ...
  yield 3
}
 
g = generator()
print(g()) // 1
print(g()) // 2
print(g()) // 3
Autant faire une méthode g1(), g2() et g3() C'est beaucoup plus simple à comprendre. Mais c'est vrai que redonner la main à la partie modèle pour gérer l'affichage ça peut être pratique... Moi perso je fais ça sur 2 threads séparés pour des questions de performances.

Les ranges, je vois pas trop... Moi j'utilise encore la bonne vielle boucle for.

PS : En C++ j'ai toujours codé seul donc mes pratiques de code ne doivent pas être très bonne.
Avatar de Bktero Bktero - Modérateur https://www.developpez.com
le 23/11/2018 à 8:51
Autant faire une méthode g1(), g2() et g3() C'est beaucoup plus simple à comprendre. Mais c'est vrai que redonner la main à la partie modèle pour gérer l'affichage ça peut être pratique... Moi perso je fais ça sur 2 threads séparés pour des questions de performances.
Si je ne m'abuse, ce n'est pas la même chose. Le but n'est pas de produire 3 résultats mais de produire une suite de résultats, un peu comme si tu avais un objet avec une fonction next(). Tu n'as pas à savoir si tu dois appeler g1(), g2() ou g3(), tu appelles simplement g(). Et surtout là tu parles de 3 fonctions mais que faire quand il y a 1000 résultats possibles ? C'est un peu l'idée d'une fonction rand().
Avatar de Matthieu76 Matthieu76 - Membre éclairé https://www.developpez.com
le 26/11/2018 à 18:41
Citation Envoyé par Bktero Voir le message
Si je ne m'abuse, ce n'est pas la même chose. Le but n'est pas de produire 3 résultats mais de produire une suite de résultats, un peu comme si tu avais un objet avec une fonction next(). Tu n'as pas à savoir si tu dois appeler g1(), g2() ou g3(), tu appelles simplement g(). Et surtout là tu parles de 3 fonctions mais que faire quand il y a 1000 résultats possibles ? C'est un peu l'idée d'une fonction rand().
Technique de noob :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
 
void DoAllTasks()
{
        auto result = g1();
        cout << result << endl;
        auto result = g2();
        cout << result << endl;
        auto result = g3();
        cout << result << endl;
}
Ça marche bien mais le problème dans ce que je viens d'écrire c'est la répétition du cout, comment faire pour ne pas le répéter. hum ....

Un truc du genre peut-être ?
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 
void DoAllTasks()
{
        for(int i = 0; i < 3; i++)
       {
            auto result = DoTask(i);
            cout << result << endl;
        }
}
 
string DoTask(int i)
{
    if(i == 1)
        return g1();
    if(i == 2)
        return g2();
    if(i == 3)
        return g3();
}
Mais j'avoue que dans ce cas c'est pas top de faire ça. Sinon on peut aussi passer la function cout en paramètre de g1, g2 et g3 mais ça serait encore moins compréhensible qu'un coroutine.

Tout ce pour dire : Quand on ne sait pas que les coroutines existe, on en a pas besoin car on réfléchit sans. Et c'est pareil pour beaucoup de concepts "pas forcément utile" : switch case, try catch, assertion, etc. C'est tout le contraire de d'autres concepts dont j'ai tout de suis vu l'utilité et comment les intégrer à mon code : tableau, objet, héritage, héritage-multiple, etc.
Avatar de Luc Hermitte Luc Hermitte - Expert éminent sénior https://www.developpez.com
le 26/11/2018 à 19:09
Les générateurs prennent vite de l'intérêt avec les ranges. Dernièrement j'ai eu à comparer deux séquences ordonnées pour trouver les trous. C'est un parcourt concurrent classique (celui que l'on dans le merge-sort). Sauf que mes séquences, c'était 8 boucles imbriquées qui les généraient. Pour le coup, yield, c'est bien pratique.

Pour les ranges, j'aime énormément la page de présentation de la preuve de concept (fort avancée) : https://ericniebler.github.io/range-v3/index.html On voit vite ce que cela peut offrir.

Concept et contrat, c'est très proche sur le sujet de la certitude de faire des choses correctes. Mais le moment visé n'est pas le même.
Avatar de foetus foetus - Expert éminent https://www.developpez.com
le 26/11/2018 à 19:33
Citation Envoyé par Matthieu76 Voir le message
Ca marche bien mais le problème dans ce que je viens d'écrire c'est la répétition du cout, comment faire pour ne pas le répéter. hum ....


Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <iostream>
 
 
class Generator {
public:
 
    Generator() : count(0) {}
 
 
public:
 
//  yield
    std::string operator() () {
        std::string result;
 
        switch(count) {
        case 0:  result = g1(); ++count; break;
        case 1:  result = g2(); ++count; break;
        case 2:  result = g3(); ++count; break;
        default: result = std::string(""); break;
        }
 
        return result;
    }
 
 
    void yield_print_all() {
        std::cout << p_yield_all() << std::endl;
    }
 
 
private:
 
    std::string p_yield_all() {
        std::string result, yield;
 
        result = std::string("");
        yield  = (*this)();
 
        while(!yield.empty()) {
            result += yield;
 
            yield = (*this)();
        }
 
        return result;
    }
 
 
    std::string g1() {
        return std::string("Hello ");
    }
 
 
    std::string g2() {
        return std::string("World ");
    }
 
 
    std::string g3() {
        return std::string("!");
    }
 
 
private:
 
    unsigned char count;
};
 
 
int main(/*int argc, char* argv[]*/)
{
    Generator g;
 
    g.yield_print_all();
 
    return 0;
}
Avatar de Matthieu76 Matthieu76 - Membre éclairé https://www.developpez.com
le 27/11/2018 à 10:48
Quelqu'un a-t-il encore plus compliqué à présenter ?
Contacter le responsable de la rubrique Accueil