Je participe à cette rencontre à distance encore une fois (ces dernières années je n'ai pas réussi à mettre de côté les fonds requis pour aller à ces rencontres en personne, malheureusement). La rencontre de cette semaine se tenant à Croydon (Londres, Angleterre), le décalage horaire sera mais j'ai vu pire. J'aurais normalement eu des dossiers à défendre mais j'ai été très malade en février et je n'ai pas réussi à accorder à mes dossiers le suivi dont ils auraient eu besoin, alors ça ira à cet été pour moi. De toute manière, cette semaine, le centre d'attention est C++ 26 et mes dossiers visent C++ 29.
Certains dossiers chauds à propos de la réflexivité statique demanderont des efforts, et les contrats demeurent controversés (c'est le dossier le plus controversé depuis des années; j'aimerais bien qu'ils deviennent officiellement partie du langage pour que nous puissions les mettre vraiment à l'essai et mieux comprendre comment bien les utiliser). Je devrais passer ma semaine chez CWG comme à mon habitude.
Ce sera la première prestation de Guy Davidson à titre de Convener. La structure administrative du comité change, ce qui est normal dans les circonstances. Une modernisation notoire est que nous passons d'un Wiki commun où nous partagions tous et toutes une même identité (oui, je sais!) à un Wiki où nous sommes identifiés individuellement et où nos avons des privilèges en conséquence. C'est une bonne idée.
Tout ce qui suit est anonymisé et simplifié, dans le respect du code de conduite d'ISO en vigueur lors de nos rencontres.
Ce document est incomplet (j'ai manqué de temps). Je compléterai dès que possible.
Pour les « journaux de voyage » d'autres participants, voir :
C'est un drôle d'horaire cette semaine car le changement d'heure a eu lieu au Québec mais il se produira en Europe à la fin de la semaine. C'est moins violent pour moi (on commence à 4 h 30 heure de Montréal plutôt que 3 h 30 comme ce serait normalement le cas).
Nina Ranns fait l'accueil, puis passe le micro à Guy Davidson. Guy Davidson mentionne la présence de 27 pays cette semaine. Entre autres, on a la Russie et l'Ukraine, les États-Unis et la Chine... Beaucoup de monde! Phil Nash est notre hôte et nous dit quelques mots.
Nina Ranns présente le code de conduite, et les règles lors des votes. Le nouveau système de Wiki mène à des applaudissements bien mérités. On laisse les nouveaux membres et les invité(e)s se présenter. Pour une première fois, à ma connaissance, quelqu'un se présente comme un « producteur de contenu »; on a aussi quelques personnes de Rust qui est présent pour aider à l'interopérabilité entre nos deux langages, incluant la personne qui gère wg21.link ce qui est doublement chouette! On a aussi une représentante de Apple qui vient pour discuter d'interopérabilité avec Swift.
On présente l'organisation de la semaine et la répartition des groupes d'études comme des groupes de travail dans les diverses salles. Il semble qu'il y aura une alerte d'incendie dans le courant de la semaine (c'est gentil de nous prévenir). Il y aura quatre séances de soirée cette semaine, sur des thèmes intéressants d'ailleurs.
Le gros morceau de la semaine est le traitement des NB Comments de C++26. Le dossier le plus chaud est probablement celui des contrats, acceptés à la plus récente rencontre mais auxquels il y a encore de l'opposition (et de l'exaspération, il faut le dire).
Nina Ranns fait un rappel de l'importance de se comporter de manière humaine. Elle est vraiment excellente à cela.
La plénière se termine et les gens se déplacent vers les salles où se tiendront les travaux qui les touchent le plus. Pour moi, ce sera CWG comme à l'habitude.
(j'ai été ralenti par des ennuis de connectivité alors j'ai manqué le début de la rencontre; on fait le tri des NB Comments à traiter quand je parviens à rejoindre le groupe)
On a deux polices de caractères trop semblables pour deux familles de termes distinctes du standard, et ça a provoqué des erreurs récemment. On vise à corriger cela.
On fait le tour des sections affectées. Tout semble Ok. Davis Herring se questionne sur l'intérêt d'avoir autant de variantes de « déclarateur » dans le texte et propose une approche alternative, mais je ne pense pas qu'on va faire ça cette semaine.
Avec des changements très mineurs, on soumettra ceci pour vote samedi.
Ceci est (étrangement) ambigu :
int x = (int()) + 5;Ceci est dû à une ambiguïté (est-ce une signature de fonction ou est-ce un int par défaut?). On a d'autres cas :
struct T { int operator++(int); T operator[](int); };
int a = (T()[3])++; // not a cast
(S())[]->A<int>; // OK, constructor call
(S())[]->A<int> {return {};}; // error: C-style cast of lambdaOn examine la proposition de changement à la grammaire pour résoudre le tout (ça introduit un terme grammatical amusant : nofun). Ça change un exemple :
template <class T> struct X {};
template <int N> struct Y {};
X<int()> a; // type-id
X<int(1)> b; // expression (ill-formed)
Y<int()> c; // type-id (ill-formed)
Y<int(1)> d; // expression
void foo(signed char a) {
sizeof(int()); // type-id (ill-formed) expression
sizeof(int(a)); // expression
sizeof(int(unsigned(a))); // type-id (ill-formed) expression
(int())+1; // type-id (ill-formed) expression
(int(a))+1; // expression
(int(unsigned(a)))+1; // type-id (ill-formed) expression
}
typedef struct B { int C[2]; } *B, C;
void g() {
sizeof(B()->C[1]); // OK, sizeof(expression)
sizeof(auto()->C[1]); // error: sizeof of a function returning an array
}On examine la grammaire résultante et certaines pensent que ça brise les déclarations de fonctions avec trailing return types. On travaille un peu et on arrive à résoudre cette préoccupation. Ça nous prend le reste de l'avant-midi. On penche pour Tentatively Ready mais c'est un changement compliqué alors on hésite. John Spicer suggère de viser C++29 mais d'en faire un DR, pour se donner le temps de réfléchir.
(je dois ensuite quitter pour la journée car j'ai un rendez-vous médical de routine puis j'enseigne).
Je me lève vers 3 h du matin pour lire ce qui s'est passé hier après-midi. Un gros dossier est celui des valeurs consteval ou des types consteval : on a présentement des types consteval-only, utiles entre autres pour la réflexivité, mais une approche concurrente qui semble prometteuse est celle de valeurs consteval-only. On peut voir des avantages à cette nouvelle approche, mais il est très tard dans le cycle de C++26 pour un tel changement et il y a des tenants des deux approches.
C'est merveilleux que nous ayons des scribes pour ces rencontres. Ça permet de suivre les débats.
La rencontre débute et Jens Maurer nous explique que nous devrons nous joindre à EWG plus tard aujourd'hui pour porter la parole de CWG sur cet important sujet. On attire aussi notre attention sur Core Issue 2675 qui avait une formulation interférant avec du texte chez LWG. Une reformulation est proposée.
On n'est pas clairs sur nos intentions quant à des cas comme ceci :
constexpr bool b = "abc" == "abc";Ce qui est sur la table est :
constexpr const char *f() { return "foo"; }
constexpr bool b1 = "foo" == "foo"; // error: non-constant
constexpr bool b2 = f() == f(); // error: non-constant
constexpr const char *p = f();
constexpr bool b3 = p == p; // OK, value of b3 is true
constexpr bool b4 = "xfoo" + 1 == "foo\0y"; // error: non-constant; string literal object could contain "xfoo\0y"
constexpr bool b5 = "foo" == "bar"; // OK, value of b5 is false
constexpr bool b6 = "foo" == "oo"; // OK, value of b6 is false; offsets would be different in a merged string literal object
extern int i;
constexpr std::initialized_list<int*> il1{ &i };
constexpr std::initialized_list<int*> il2{ &i };
constexpr std::initialized_list<int*> il3{ (int*)nullptr };
constexpr bool b7 = *il1.begin() == *il2.begin();
constexpr bool b8 = il1.begin() == il3.begin(); // error: non-constantÇa mène à l'étrange idée d'un type de constexpr-unknown representation pour des trucs comme les pointeurs, les union et les objets volatiles, et de potentially non-unique object pour les trucs comme les pointeurs dans un littéral texte.
On travaille sur l'exemple car on veut clarifier les diverses facettes de la règle proposée. Hubert Tong propose ceci :
#include <initializer_list>
extern int i;
constexpr std::initializer_list<int *> il1{ &i };
constexpr std::initializer_list<unsigned long> il2{ 0 };
static_assert(il1.begin() == (void *)il2.begin()); // ill-formedAprès une bonne heure de teavail, on arrive à un niveau de satisfaction acceptable. On amènera ça pour vote samedi.
L'enjeu est que nous avons une définition de expansion-iterable qui, sous sa forme actuelle, peut provoquer des instanciations de templates et ralentir la compilation. Une proposition a été soumise sous la forme de P4026 et cette proposition fut acceptée par EWG.
On a un enjeu ici parce qu'il est possible de mettre #line 2147483648 qui dépasse la valeur maximale d'un entier signé sur 32 bits et que ceci mène à du comportement indéfini. La proposition est de rendre ceci conditionally supported, mais quand on dit ça il faut indiquer ce qu'on fait quand ce n'est pas supporté.
On a le problème que ceci est ill-formed:
struct K {
void f(this K);
void f();
};... alors que ceci, essentiellement équivalent, ne l'est pas :
struct OK {
void f(this OK);
void f() &;
};La nuance est dans la définition de « correspond » dans le texte du standard. P4138 couvre en détail l'interaction de correspond avec les ref-qualifiers de fonctions membres. On examine ce qui y est décrit. On passe le reste de la première partie de la journée à examiner l'approche mise de l'avant par cette proposition.
(brève pause matinale)
Après la pause, on discute de la proposition et on pense que EWG devrait la regarder. La proposition rejoint la vision de CWG et nous sommes disposés à travailler sur le Wording qu'elle comporte, mais on souhaite l'avis de EWG avant de procéder.
L'auteur est parmi nous maintenant. On blague parce que le Wording n'est vraiment pas terrible, mais l'essentiel est préexistant et n'est pas de sa faute (ses correctifs sont pertinents et tout petits). La proposition sera portée au vote samedi.
(je dois m'absenter une heure environ car Ludo, mon plus jeune, a manqué son autobus; je reviens tout juste avant la fin de l'avant-midi à Croydon et on discute de P3865)
Je n'ai pas le contexte mais on débat de la validité de certains exemples. C'est un sujet très abstrait. L'exemple suivant semble provoquer des réactions :
template <typename ... Ts>
struct Y {
Y();
Y(Ts ...);
};
template <template<typename T = char> class X>
void f() {
X x0{}; // OK, deduces Y<char>
X x1{1}; // OK, deduces Y<int>
X x2{1, 2}; // error: cannot deduce X<T> from Y<int, int>
};
template void f<Y>();On en reparlera. C'est l'heure du dîner, et l'après-midi reprendra chez EWG (j'espère être de retour à temps; mon amoureuse Za et moi avons une vaccination de chien à faire pendant le « dîner » londonien).
(à mon retour du vaccin de notre chien Pauline, Barry Revzin explique P4101 qui décrit le modèle consteval-only values chez EWG et les membres de CWG sont présents)
Les débats autour du modèle consteval-only values en comparaison avec le modèle consteval-only types sont costauds. Un enjeu est que nous devons faire un choix car sinon, les implémentations planteront devant les cas d'utilisation proposés. Dan Katz fait remarquer que la spécification existante exige dans certains cas que le compilateur soit un oracle tout puissant (par exemple la réflexivité sur un pointeur extern). Certains cas de méthodes virtuelles consteval sont possibles seulement dans un monde d'évaluation consteval ce qui peut causer des dégâts si le monde compile-time se met à fuir dans le monde run-time. Il soulève aussi la question de l'identité d'une reflection, par exemple dans le cas où on comparerait une version run-time et une version compile-time d'une « même » reflection.
Ensuite, Wyatt Childers présente P4135 qui décrit les mérites du modèle consteval-only types. Il commence par dire que le modèle consteval-only values est intéressant mais si dit d'avis qu'on réagit trop fort selon lui. Il parle de son expérience en métaprogrammation et dit avoir étudié sous Andrew Sutton. Avec quelques exemples, il met de l'avant son intérêt pour permettre de réaliser des solutions simples aux problèmes qu'il rencontre et montre des cas qui devraient fonctionner mais ne le font pas. Ses exemples profitent du fait (par exemple) qu'un type holder<T> pour une type T qui soit consteval-only devient implicitement consteval-only ce qui peut mener à de meilleurs diagnostics et à moins de bruit dans le code source. Il montre qu'il a implémenté son approche en deux jours sur le compilateur d'EDG et qu'il obtient des diagnostics de qualité. Une partie forte de son argumentaire est que sa proposition repose sur le statu quo et que C++26 se termine cette semaine.
Les séances de questions / réponses des deux propositions sont intéressantes. On est en terrain extrêmement novateur et les cas limites se cachent dans les ombres. Shafik Yaghmour signale qu'en tant qu'implémenteur, l'approche consteval-only values lui semble plus facile à comprendre et à implémenter, mais à son avis clang n'est pas encore proche d'implémenter le tout alors il ne voit pas l'urgence de prendre un vote. D'autres passent au micro et disent aussi avoir un faible pour l'approche consteval-only values mais préférer ne pas trancher aujourd'hui.
John Spicer rappelle qu'on parle d'une décision très importante et défend l'idée de prendre son temps. Il ne se dit pas d'avis qu'il faille se presser dans ce cas. Il dit être arrivé à la conclusion que nous aurons éventuellement des consteval-only types et pense qu'il faudrait réfléchir à la possibilité d'avoir un jour les consteval-only values aussi.
Corentin Jabot défend l'idée que laisser des std::meta::info glisser vers le monde run-time ne cause pas de dégâts. Tout ce qu'on peut faire avec cela, c'est les comparer. Il se dit aussi plus confiant en sa capacité d'implémenter le modèle consteval-only values avec clang. Il signale qu'un enjeu avec le design consteval-only types est que le fait qu'un type soit consteval-only ou pas dépend du moment où ce type est observé.
(brève pause du milieu de l'après-midi)
On signale que présenter ces options devant SG20 pour discuter de l'aisance d'enseignement serait pertinent, ce qui ajouterait à l'intérêt de prendre son temps.
Gabriel Dos Reis pense que dans un cas comme dans l'autre, ces bug fixes cachent des nouveaux features. Il dit que la spécification actuelle de immediate escalation est en partie incohérente et n'est vraiment pas simple à implémenter. Selon lui, mieux vaut ne pas trancher aujourd'hui.
Daveed Vandevoorde, pour sa part, dit qu'en tant qu'implémenteur il souhaite qu'une décision soit prise par EWG. Il préfère le modèle consteval-only values mais la priorité pour lui est de savoir quoi faire. Barry Revzin se dit d'avis quele modèle consteval-only values couvre tous les cas que le modèle consteval-only types couvrirait, en plus de quelques cas que nous devrons couvrir de toute manière. Hubert Tong rappelle que le regard de CWG ne s'est posé sur l'approche consteval-only values qu'hier, pas avant; à son avis, le leakage n'est pas la clé ici mais ce qui compte vraiment est de s'assurer qu'un appel de fonction immédiate le demeure, et explique que c'est avec le modèle consteval-only types qu'on a cette certitude le plus directement.
(je ne prends pas toutes les interventions car il y en a vraiment beaucoup)
Jakub Jelinek se dit d'avis que si on conserve le statu quo, on aura le modèle consteval-only types et que cela sera difficile à changer par la suite. Herb Sutter rejoint Gabriel Dos Reis et John Spicer et préfère attendre. Corentin Jabot mentionne qu'à court terme, seul gcc aura la réflexivité alors le plus important est de savoir ce que sera l'effet de nos décisions sur ce compilateur. Jason Merrill signale que peu importe la décision, la réflexivité sera expérimentale alors on pourra faire des changements. (on a des gens qui vont jusqu'à se questionner si la réflexivité est prête à faire son entrée officiellement, mais ça m'étonnerait qu'on aille jusque là; John Spicer fait d'ailleurs un rappel qu'on parle de cas limites ici, et que ça ne devrait pas disqualifier l'ensemble de l'oeuvre)
On vote sur l'idée de trancher cette semaine, et il n'y a pas de consensus. On garde donc le statu quo pour le moment sur ce plan. On prend ensuite un vote d'encouragement pour l'une ou l'autre des deux approches, question d'aider les implémenteurs à s'orienter, et l'approche consteval-only values semble plus populaire pour le moment mais on verra pour le futur.
(CWG quitte la pièce; quand la discussion reprend chez CWG, on discute des contrats qui semblent ne pas avoir été modifiés et être en route vers C++26)
Il semble que EWG ait décidé que non, alors notre travail est d'attendre une version mise à jour de la proposition et tenant compte de ce choix directionnel. Brian Bi ne semble pas convaincu que EWG ait compris qu'on avait aussi besoin d'une décision sur les valeurs par défaut des valeurs par défaut des variables membres d'un agrégat. (note plus tard dans l'après-midi : ça semble être le cas)
Jens Maurer suggère qu'on attende que EWG prenne une décisions avant de faire quoi que ce soit ici.
Ce qui suit retourne 1 dû au moment où l'instantiation du type survient, mais ce n'était pas l'intention :
struct S;
consteval size_t f() {
constexpr bool b = is_complete_type(^^S);
return b ? 1 : 2;
}
struct T;
consteval {
define_aggregate(^^S, {});
define_aggregate(^^T, {
data_member_spec(^^int, {.name="m", .bit_width=f()}),
});
}
int main() {
return bit_size_of(^^T::m);
}Le changement proposé est principalement un déplacement de passages du standard pour faire en sorte que l'ordre dans lequel les règles sont appliquées rejoignent mieux l'idée initiale. On penche pour marquer le tout Tentatively Ready pour le moment puis on lit le texte avec attention. C'est délicat parce que déterminer le point d'instanciation d'un type détermine ce qu'on peut faire par réflexivité avec ce type (on fait une note non-normative pour préciser cela).
Dan Katz va travailler là-dessus ce soir pour qu'on puisse le travailler demain.
On reprend les travaux vers 4 h 30 heure de Montréal. Les discussions sur les modèles consteval de la veille sont encore actives dans la salle.
Jens Maurer explique que ceci lui semblait évolutionnaire, donc il l'a envoyé à EWG qui a indiqué que ceci n'était pas une défectuosité : le comportement obtenu est volontaire et délibéré. Certains sont en désaccord alors il se peut que ce soit rediscuté aujourd'hui.
Il y a eu des changements depuis hier car le texte menait à une récurrence infinie.
On a cet exemple qui teste la complétion d'un type S :
consteval int f(int p) {
struct S;
constexpr int r = /* P */ std::meta::is_complete_type(^^S) ? 1 : 2;
consteval {
std::meta::define_aggregate(^^S, {});
}
return (p == 0) ? f(p - 1) : r;
}
static_assert(f(1) == 2);On essaie de comprendre ce que l'exemple cherche à démontrer (est-ce que le type est toujours incomplet quoi qu'il advienne?). On examine la proposition de correctif « anti récurrence infinie » pendant un certain temps (ça donne lieu à une bonne blague quand Jens Maurer dit « you can keep staring and tell me when you're done » et Jason Merrill réplique, en parlant lentement, « I think it's staring back at me... » (un moment Nietzschéen parfait!).
On remarque que le texte suppose un ordre entre certaines opérations internes de l'acte de compiler, d'une manière pour laquelle le reste du standard ne prend pas position. C'est peut-être une bonne chose de clarifier cet aspect. On en discute un peu (l'avènement de la réflexivité statique nous force à examiner notre propre mécanique de plus près car elle révèle, par sa nature-même, des aspects du processus de compilation qui étaient invisibles auparavant).
On va retravailler là-dessus cet après-midi en la présence Dan Katz pour nous assurer que nous avons bien compris son intention.
On passe un peu de temps à regarder les NB Comments qu'on pourrait traiter cette semaine, et à placer certains d'entre aux à l'agenda cette semaine (si on ne les traite pas, ils ne seront pas dans C++26).
On a un problème avec des cas comme celui-ci :
template<typename ... T> char *f(T &...); // #1
template<typename T> int *f(T &&); // #2
int i;
auto *p = f(i);On veut que #2 soit choisi, mais les règles disent #1 ce qui est nuisible. On a des divergences d'implémentation. En gros, c'est un changement qui brise trop de code.
Les changements terminologiques consistent pour l'essentiel à un retour au statu quo pré-CWG 1395 pour la moitié du texte. On a aussi un problème avec des cas comme :
template<class... Args> void f(Args... args); // #1
template<class T1, class... Args> void f(T1 a1, Args... args); // #2
template<class T1, class T2> void f(T1 a1, T2 a2); // #3
f(); // calls #1
f(1, 2, 3); // calls #2
f(1, 2); // calls #3; non-variadic template #3 is more specialized
// than the variadic templates #1 and #2... car le texte ne nous aiguille pas correctement vers la bonne fonction dans certains cas.
On prend un bon moment à regarder les règles proposées pour voir si elles nous mènent là où on souhaite aller. Ça semble être le cas. Génial! On en fera un DR et ce sera amené pour vote samedi.
Ce que cette proposition vise à permettre est ceci :
template<typename ... T>
struct C {
C(T ...);
};
template<template<typename> class X>
void f() {
X x1{1};
X x2{1, 2};
}
template<typename T>
using XC = C<T>; // exposition only
template<> void f<C>() {
XC x1{1}; // OK, deduces C<int>
XC x2{1, 2}; // error: class template argument deduction fails
}... ou encore, avec paramètres par défaut :
template< typename T = int>
struct C {
C(int);
};
template<template<typename = long> class X>
void f() {
X x{1};
}
template<typename T = long>
using XC = C<T>; // exposition only
template<> void f<C>() {
XC x{1}; // OK, deduces C<long>
}On examine quelques cas préexistants qui fonctionnent en pratique (on ne voudrait pas briser du code légal). Un enjeu est que certains cas sont très difficiles à analyser, par un compilateur comme par un humain.
(brève pause; on est à la frontière de CWG et de EWG alors c'est difficile)
On remarque que la proposition briserait un exemple pris de CWG 3003 :
template <typename T> struct A { A(T); };
template <typename T, template <typename> class TT = A>
using Alias = TT<T>; // <-- ceci est méchant; certains pensent que c'est le mal
template <typename T>
using Alias2 = Alias<T>;
void h() { Alias2 a(42); }
void h2() { Alias a(42); }L'alias qui est en partie auto référent s'est avéré difficile à implémenter en pratique par les compilateurs. On jongle même avec l'idée de l'interdire.
On finit avec un texte qui permet ceci, qui me semble très bien :
template<typename ... Ts>
struct Y {
Y();
Y(Ts ...);
};
template<template<typename T = char> class X>
void f() {
X x; // OK, deduces Y<char>
X x0{}; // OK, deduces Y<char>
X x1{1}; // OK, deduces Y<int>
X x2{1, 2}; // error: cannot deduce X<T> from Y<int, int>
}
template void f<Y>();Avec des constantes, on aurait :
template<int>
struct A { };
template<int I>
struct C {
C(A<I>);
};
template<template<short> class X>
void f() {
X x1{A<1>()};
X x2{A<100000>()};
}
template<short S>
using XC = C<S>; // exposition only
template<> void f<C>() {
XC x1{A<1>()};
XC x2{A<100000>()};
}Jason Merrill indique qu'avec ceci, la réflexivité sur ces entités perd l'information sur le template template parameter. Hubert Tong explique que c'est un choix délibéré en vue d'expansions, mais on s'attend à recevoir des complaintes d'usagers éventuellement.
C'est prêt. On amènera ça pour vote samedi. Ce sera un DR.
On cherche à savoir si certaines constructions sont valides. Par exemple :
template <class T>
struct C
{
explicit C(T = 1);
};
C<int> c; // works
C<int> c(1); // works
std::cout << std::constructible_from<C<std::string>>; // <-- divergences d'implémentationLe but de la proposition est de clarifier ce qui se passe ici pour réduire les divergences.
Jason Merrill exprime un malaise quant à l'approche proposée car elle change la mécanique d'instanciation des templates. D'autres aspects du langage sont touchés, par exemple les spécifications noexcept, les précondition et les postconditions, certaines parties des lambdas, les annotations, etc. Brian Bi propose l'exemple suivant : https://godbolt.org/z/5oeWz84Eb donc :
template <lclass T>
auto foo() -> decltype([]() noexcept(noexcept(T() + 1)){}());
template <class T>
concept canFoo = requires { foo<T>(); };
static_assert(canFoo<int>); // <-- divergences d'implémentation
struct X {};
static_assert(!canFoo<X>); // <-- divergences d'implémentationLes divergences tiennent au traitement (ou pas) de la clause noexcept dans le immediate context. L'approche proposée semble briser des implémentations légitimes d'instanciations de lambdas et de contrats. Une solution proposée serait d'instancier une lambda en deux temps (d'abord la lambda en soi, ensuite son opérateur ())
(ce sont de longs mais intéressantes débats; pause pour le dîner. Dan Katz est de retour en début d'après-midi alors nous pourrons travailler sur ses propositions)
Brian Bi dit que sa compréhension des échanges avec Jason Merrill est que le souhait est que seules les lambdas devraient avoir un traitement spécial dans ce dossier; les autres volets discutés (paramètres avec valeurs par défaut; clauses noexcept; annotations, contrats, etc.) devraient avoir un traitement uniforme. Jason Merrill semble d'accord pour l'essentiel.
John Spicer pense que dans le cas d'une lambda, il faut substituer l'entièreté de la lambda pour comprendre le type de l'expression ce qui suggère un traitement semblable à celui des classes locales aux fonctions, mais n'est pas convaincu que le même traitement doive être accordé aux contrats. Un enjeu sous-jacent à cette discussion est de savoir si une erreur devrait disparaître à travers SFINAE ou provoquer une hard error, et notre lecture des besoins en ce sens guide en partie nos choix.
L'interaction avec les contrats et les surcharges de fonctions est explorée : si le contrat est évalué à la compilation et la fonction est appelée hors-contrat (pre(expr) où expr == false) on aurait, selon la compréhension de John Spicer, un hard error, et ce même si la fonction n'est pas appelée (par exemple, si on cherche à bonne surcharge d'une fonction et qu'on doit évaluer des trucs comme le corps d'une lambda ou une clause noexcept). On n'est pas sûrs que les gens qui ont conçu les contrats ont vu venir ce coup; ça ne semble pas indiqué où que ce soit, mais les membres de CWG sont partagés à ce sujet.
On revient au texte sur la table (les débats – super intéressants – ont duré plus d'une heure!). Ce qui est proposé semble rassembleur : les trucs qui requièrent une instanciation séparée (lambdas, noexcept, valeurs par défaut des paramètres, annotations) ne feraient pas partie du contexte immédiat et provoqueraient, le cas échéant, leurs propres erreurs.
On va passer quelques questions à EWG et à l'auteur officiel de la proposition et revenir dessus, peut-être plus tard cette semaine, avec l'information que ça nous apportera.
(l'un des principaux auteurs des contrats nous informe que l'intention est un hard error, même dans le cas de surcharges de fonctions, mais le débat redémarre... C'est une question de fond, et les contrats sont un sujet qui touche tout le monde!)
On revient sur ceci :
consteval int f(int p) {
struct S;
constexpr int r = /* P */ std::meta::is_complete_type(^^S) ? 1 : 2;
consteval {
std::meta::define_aggregate(^^S, {});
}
return (p == 0) ? f(p - 1) : r;
}
static_assert(f(1) == 2);Dan Katz explique que son idée pour l'évaluation de r à chaque passage tient au contexte d'évaluation, qui est conceptuellement distinct à chaque appel. Il pense toutefois qu'on peut le traiter comme un as if et ne l'évaluer qu'une seule fois.
Jason Merrill demande où se trouve le point d'instanciation P concrètement. Davis Herring et Dan Katz expliquent que le consensus pour le moment est « à la fin de l'unité de traduction », mais que ça pourrait changer dans le futur. On s'entend pour remplacer P par Q car P a un autre sens dans la prose qui accompagne l'exemple.
Jason Merrill demande si la réponse à la question std::meta::is_complete_type(^^S) serait différente si on la posait après le bloc consteval, et Dan Katz dit que c'est effectivement le cas.
On retravaille l'exemple :
consteval int f(int p) {
struct S;
constexpr int r = /* Q */ std::meta::is_complete_type(^^S) ? 1 : 2;
consteval {
std::meta::define_aggregate(^^S, {});
}
static_assert(std::meta::is_complete_type(^^S));
return (p == 0) ? f(p - 1) : r;
}
static_assert(f(1) == 2);Après la brève pause de l'après-midi, on change légèrement le tout pour que ça porte un peu plus d'information :
consteval int f(int p) {
struct S;
constexpr int r = /* Q */ std::meta::is_complete_type(^^S) ? 1 : 2;
consteval {
std::meta::define_aggregate(^^S, {});
}
static_assert(std::meta::is_complete_type(^^S));
return (p == 0) ? f(p - 1) : r;
}
consteval {
if(f(1) != 2)
throw; // Ok, not evaluated
}Après quelques heures de travail, on convient que ça peut être amené pour vote samedi. Ouf!
Ouf encore : on nous a envoyé un document de 101 pages à examiner à ce stade dans la semaine, et sur un sujet profond... Davis Herring nous explique que sa compréhension est que les entités atomiques Out of Thin Air requises pour réaliser certaines opérations et qui, en théorie, peuvent provoquer des conditions de course ne sont pas, en pratique, un réel problème... le tout avec beaucoup de mathématiques. Heureusement, la version qui nous est transmise se limite à 15 pages (elle a été éditée pour être digeste).
Jens Maurer suggère que, le texte reflétant l'état actuel de la recherche contemporaine sur le sujet, et que le texte a été traité par SG1 et EWG déjà, on ne pourra probablement pas trouver de meilleure terminologie que ce qui est proposé. La principale tâche pour nous est une note, pour laquelle certains identifient des enjeux (éditoriaux). Le texte dit essentiellement (et informellement, dans ce cas) qu'un compilateur ne produira pas de OOTA pour un programme qui ne contient pas de comportement indéfini.
On en discutera en présence des auteurs.
C'est une proposition de clarification d'un texte qui, de prime abord, semble exclure les return alors que l'intention semble plutôt être de les inclure (le texte auquel ça réfère définit le point où les objets locaux à une fonction sont détruits). L'ajustement de texte proposé est pas mal (et est simple) mais pourrait causer d'autres ennuis dans un contexte plus large, par exemple une double destruction d'objets lors d'une levée d'exception, alors on ajuste légèrement. C'est difficile (de manière peut-être surprenante) de définir clairement l'endroit où une fonction se termine... Roger Orr soulève le cas de longjmp() et suggère une référence croisée.
Après un peu de travail, on décide que c'est prêt et que ce sera un DR. Vote samedi
Paul McKenney se joint à nous. Jens Maurer lui demande où on doit insérer ces trucs très abstraits dans le texte du standard (au moins : un numéro de section). On s'entend pour juste après §32.5.4/9, [atomics.order]. On discute de certains choix de termes dans la note comme « runs on real-world hardware » qui semble surtout chercher à éviter des machines abstraites qui auraient un comportement hostile ou déraisonnable et on cherche des alternatives plus à propos.
On relate la présence et le rôle des variables atomiques (en C++, on peut être atomique et volatile, ce que d'autres langages comme Rust ne permettent pas), mais il se trouve que sans variables volatiles, des trucs comme les cartes de crédit ne fonctionnent pas.
On passe presque une heure sur une note non-normative qui décrit les différences fondamentales entre une machine abstraite et une machine « réelle », mais ça vaut la peine et le texte résultant est visiblement meilleur qu'à l'origine, même aux yeux de son auteur. On laisse Paul McKenney faire les retouches, les présenter à LWG et, si tout le monde est content, on le soumettra au vote samedi.
Barry Revzin se joint à nous pour présenter ceci. Cette proposition corrige des bogues introduits dans une proposition antérieure qui rendait les union plus utiles, surtout dans un contexte constexpr. On joue avec les règles de vie d'un union et de ses membres, alors c'est assez profond. On change entre autres la terminologie, parlant désormais de union subobjects en plus de (et non pas en remplacement de) union members, ce qui a la qualité de rapprocher la terminologie des union de celle des class. On définit aussi ce qu'est un sous-objet inactif (on ne parlait auparavant que du membre actif).
Le traitement des tableaux multidimensionnels et de ce que peut être un inactive subobject dans ce cas demande un peu de temps. Il ne faut pas oublier qu'en gérant la mémoire de manière « serrée » on peut avoir un tableau dont certains éléments sont actifs et d'autres ne le sont pas.
Barry Revzin explique brièvement l'historique de cette proposition. On trouve un nouvel exemple qui montre la portée d'une annotation :
void f([[=1]] int x);
void f([[=2]] int y) {
constexpr info rp = parameters_of(^^f)[0];
constexpr info ry = variable_of(rp);
static_assert(ry == ^^y);
static_assert(annotations_of(rp).size() == 2); // both [1, 2]
static_assert(annotations_of(ry).size() == 1); // just [2]
static_assert(annotations_of(rp)[1] == annotations_of(ry)[0]); // both yield 2, and it's the same 2
}On voit ici que les annotations sur la déclaration et sur la définition de f() sont retenues dans la définition quand on regarde annotations_of(rp).
Le reste du document a été vu par d'autres groupes en plus de CWG. On devrait pouvoir soumettre ça au vote samedi; reste juste à décider si c'est soumis par CWG ou LWG. On verra.
À suivre demain...
Jens Maurer explique que notre tâche principale aujourd'hui inclut l'examen des Core Issues marquées Tentatively Ready, et de traiter certaines questions qui ont été amenées à son attention depuis hier soir. On évacue rapidement quelques NB Comments pour lesquels on n'a pas de proposition de résolution à l'horizon, faisant des Core Issues de ceux-ci pour les traiter ultérieurement.
Les efforts pour « Define "immediate context" » ont été envoyés à EWG.
Pour le moment, deux déclarations d'une même entité qui ne sont pas mutuellement attetignables (Reachable) dans deux modules distincts mène à une situation de IFNDR. Jason Merrill propose une résolution clarifiant les règles dans ce cas. Avec ses mots, on obtient par exemple :
// "decls.h":
int f(); // #1, attached to the global module
int g(); // #2, attached to the global module
// Module interface of M:
module;
#include "decls.h"
export module M;
export using ::f; // OK, does not declare an entity, exports #1
int g(); // error: matchescorresponds to #2, but attached to M
export int h(); // #3
export int k(); // #4
// Other translation unit:
import M;
static int h(); // error: matchesconflicts with #3
int k(); // error: matchesconflicts with #4J'ai manqué quelques minutes de débats mais les changements terminologiques sont petits et ciblés. On prend tout de même le temps de les regarder en détail.
On va en faire une Core Issue marquée Tentatively Ready, puis l'apporter au vote pour samedi en tant que DR.
Jens Maurer explique que l'on pensait ceci Ready pour samedi, mais Hubert Tong a constaté des problèmes durant la nuit. Hubert Tong explique d'ailleurs qu'on a vérifié tout ce qui était « bon » mais qu'on a échappé des trucs « mauvais », où on gère mal des non-déductions. Il explique le problème et donne une idée d'une possible solution (en gros, il faut exiger que le paramètre problématique soit un Deducible Template).
On discute de la possibilité de faire l'ajustement mineur (ajouter une petite phrase) sans changer le numéro de révision de la proposition et d'expliquer la situation en plénière, mais on décide de simplement faire une nouvelle révision du document (et de trouver comment enlever la révision précédente du Wiki, car c'est un nouveau Wiki et on n'est pas pleinement familières et familiers avec son fonctionnement).
Jens Maurer explique qu'on avait fait de ceci un DR, mais les règles font que les destructeurs peuvent devenir des fonctions immédiates ce que le langage ne permet pas (on ne sait pas quoi faire avec les variables globales) alors on a une restriction à leur sujet. Il se trouve que Tim Song a signalé que cette situation n'est pas utile car si on a un vector<meta::info> on doit en détruire les éléments et ceux-ci sont consteval ce qui mène à un hard error.
On indique en réponse que résoudre ceci dépend de résoudre la question des modèles consteval-only types vs consteval-only values ce qui attendra manifestement à C++29.
On a ceci :
#include <span>
constexpr int arr[3] = { 1, 2, 3 };
consteval std::span<const int> foo() {
return std::span<const int>(arr);
}
int main() {
int r = 0;
template for (constexpr auto m : foo())
r += m;
}
// The expansion of this iterable expansion statement contains:
//
// constexpr auto &&range = foo();
//
// The deduction yields std::span<const int>&& for the type of range, and
// the non-const lifetime-extended temporary causes constant evaluation to fail.On regarde le problème qui est bien exploré par la personne ayant soumis le Core Issue. Faudra probablement changer la manière dont on expansion-iterate à travers certains types, entre autres. Une partie de la solution est de faire en sorte que le template for fasse une copie du std::span<const int> à traverser plutôt qu'un std::span<const int>&&.
Hubert Tong indique que la proposition n'est pas claire sur les types qui bénéficieraient (ou pas) d'un tel changement. Jason Merrill signale qu'avec decltype(auto) et une prvalue, on n'a pas de copie en pratique.
On prend le temps d'examiner les cas limites (il y a beaucoup de trucs itérables en C++). Ça semble pas mal dans l'ensemble.
La Core Issue demande de préciser l'emplacement du Padding dans une classe, à travers un nouveau terme Property of the Implementation qui veut essentiellement dire que c'est implementation-defined, mais sans exiger que les implémentations ne documentent leurs choix. Ça impose toutefois de ne pas changer d'idée avec chaque unité de traduction.
Ça semble Ok. Ready, sous forme de DR.
On a ceci :
#include <new>
struct A { unsigned char buf[1]; };
static_assert(sizeof(A) == 1); // A can fit within A::buf
int main() {
A x{};
new (x.buf) A{};
}Augh! Exploration supplémentaire :
Faut clarifier ce qu'on veut dire par « deux objets ont la même adresse ». La proposition fonctionne. Ready, DR.
Soit le code suivant :
struct A { int n; };
struct B : A {
using A::A;
B(int);
};Est-ce que B a un constructeur par défaut? Jens Maurer est d'avis que le texte préexistant couvre ce cas, mais une petite clarification est ajoutée pour aider à la compréhension.
Le texte est pas mal. Ready, DR.
(brève pause du matin)
Dans un monde de modules, ceci ne joue pas gentiment avec le reste du langage :
#define DOT_BAR .bar
export module foo DOT_BAR;On veut changer la grammaire pour tenir compte de ce cas car l'intention est de pouvoir identifier un nom de module rapidement et sans recours au préprocesseur.
Avec la résolution proposée, on aurait :
// Exemple :
#define DOT_BAR .bar
export module foo DOT_BAR; // error: expansion of DOT_BAR; does not begin with ; or [
// Exemple :
#define MOD_ATTR [[vendor::shiny_module]]
export module M MOD_ATTR ; // OK
// Exemple :
export module a
.b; // error: preprocessing token after pp-module-name is not ; or [
// Exemple :
export module M [[
attr1,
attr2 ]] ; // OK
// Exemple :
export module M
[[ attr1,
attr2 ]] ; // OK
// Exemple :
export module M; int
n; // OKCe sera un DR porté au vote samedi.
C'est amusant ça : le texte du standard spécifie sizeof(std::nullptr_t) mais pas alignof(std::nullptr_t). La proposition est de les faire correspondre tous deux à ceux de void*. Ça correspond à ce que fait C23. Certains mentionnent que cela peut pessimiser les implémentations d'outils traitant du JSON à travers des std::variant par exemple. On va de l'avant malgré tout.
On indique que le texte les présente comme des variables, mais ce n'est pas correct. On clarifie le tout. Ready, DR.
Un autre cas de clarification pour lequel le texte semble correct. Ready, DR.
Notre description de ce qui se produit dans un cas comme le suivant est perfectible
int foo() {
struct S {
~S() noexcept(false) { throw 1; }
} s;
}
int main() {
try {
foo();
} catch (int x) {
return x;
}
}La résolution proposée est pertinente. Jason Merrill recommande qu'on l'harmonise avec nos discussions sur le moment où une fonction se termine, tenues hier. C'est une bonne idée, mais ça complique un peu le travail à faire dans ce cas. Notez que l'intérêt ici est que foo() n'a pas de return et serait du comportement indéfini, mais puisqu'elle se termine par une levée d'exception on évite cet irritant.
On traitera ça ulltérieurement, il y a trop à faire pour l'adopter cette semaine.
Bon, ce qu'on a est indigeste. La proposition est de reformuler pour que ce soit plus raisonnable. Même reformulé, c'est pas de la lecture légère. On retravaille le tout, mais la salle est partagée car plusieurs sont d'avis que le texte préexistant était plus lisible mais on sait que le texte préexistant est incorrect. On a va le mettre de côté et essayer de corriger le texte préexistant au lieu de le remplacer.
On a ceci :
void f(int p)
pre([&] { ++p; return true; }()) // error: unqualified-id 'p' has const type
pre([&] { ++[:^^p:]; return true; }()) // OK
{
}Le texte actuel ne fonctionne pas, mais EWG a dit NAD cette semaine... mais a changé d'idée il y a moins d'une heure! Conséquemment, on examine une proposition, écrite il y a deux heures à peine...
On parle ici d'un document pour corriger une décision du passé récent et qui comprendr plusieurs petits changements. L'auteur essaie de faire en sorte que l'idée soit acceptable sur le fond, quitte à retravailler la prose ultérieurement, dans le but d'en arriver à une intégration dans C++26 étant donné qu'il s'agit d'un aspect clé des contrats.
On procède. Vote samedi.
Ceci ne semble pas raisonnable :
struct A { int n; };
struct B : A { int m; };
constexpr int f() {
B b = {{0}, 0};
A *p = &b;
new (p) A{1}; // does not transparently replace *p
return p->n; // UB, p refers to out-of-lifetime object
}
constexpr int k = f();L'enjeu est que dans une fonction constexpr on demande aux compilateurs de diagnostiquer l'accès à n (le placement new est légal, mais l'exigence de rapporter que p->n est UB est déraisonnable en pratique). La proposition n'est pas de le permettre mais bien de permettre un diagnostic plus hâtif du problème. Ajouter la contrainte de transparent replaceability permettrait cela.
Ok, DR.
(pause pour le dîner)
La terminologie novatrice d'hier a été raffinée. On parle maintenant de union elemental subobjects.
On a une nouvelle fonction std::start_lifetime() qui s'ajoute à des trucs comme std::start_lifetime_as() et std::start_lifetime_as_array().
Exemple :
struct A {
struct X {
int i;
int j;
};
struct Y {
X x1;
X x2;
};
union {
int i;
int arr[4];
Y y;
};
};
constexpr A v1; // ok, no constituent values
constexpr A v2{.i=1}; // ok, the constituent values are {v2.i}
constexpr A v3 = []{
A a;
std::start_lifetime(a.arr); // ok, arr is now the active element of the union
new (&a.arr[1]) int(1);
a.arr[2] = 2;
return a;
}(); // ok, the constituent values are {v3.arr[1], v3.arr[2]}
constexpr A v4 = []{
A a;
a.y.x1.i = 1;
a.y.x2.j = 2;
return a;
}(); // error: the constituent values include v4.y.x1.j and v4.y.x2.i
// // which have erroneous valueÇa semble convenir. On soumet ça au vote samedi.
Jens Maurer nous montre la Core Issue tout juste créée pour ce dossier. On s'assure de sa conformité.
On a un texte qui est un peu confus et parfois contradictoire quant aux sortes de « caractères » pour fins d'espace à titre de mémoire brute. Le texte proposé vise à clarifier le tout, restreignant pour l'essentiel ces types à unsigned char. Faudra que je sois plus prudent (ça m'arrive d'utiliser char par négligence).
Question « simple » : que se passe-t-il si on fait ceci?
static union {
int x = [] { return 42; }();
};Ceci déclare une classe imbriquée dans cet union. Les compilateurs l'acceptent tous. On le permettra.
Le titre le dit : on a un terme utilisé pour décrire des aspects de la machine abstraite mais que l'on ne définit pas formellement. En gros, il y a un Program Point avant le premier jeton, après le premier jeton et entre toute paire de jetons. Ce sera ça pour le moment.
On resserre les règles pour empêcher de modifier le sens de macros qui changeraient le sens de mots clés. C'est désormais Ill-Formed de faire ça. Ça inclut les nouveaux mots clés de C++26 comme pre et post.
Il faut un template pour générer des expansion statements. La proposition est d'en générer une implicitement pour faire d'un template for un templated context :
struct B {
int i;
long l;
};
struct A {
B b;
};
void f(int i, long l);
int main() {
template for (auto [ ... e] : A()) {
f(e...); // OK
}
}C'est cool comme idée! On fait le tour des impacts sur la grammaire et sur la mécanique de génération de code. Ce qui est bien est que le templated context dans ce cas est bien circonscrit et ne fuit pas.
Ce qui suit devrait être value-dependent mais ne l'est pas en ce moment :
template <typename T>
consteval void f() {
T v;
std::meta::info R = std::meta::type_of(^^v); // not currently value-dependent
}Les changements demandés sont localisés et relativement simples. Merveilleux! Ready, vote samedi.
Ceci est un peu plus volumineux comme changement terminologique. On travaille à la clarification du propos. Ready, on vote samedi.
On a ceci :
#define F(X) X
#if __has_include(F(<#.h>))
#endifCeci est ill-formed mais « passe » par des moyens détournés. On ferme la brèche. Ready, DR.
Amusant. Ceci :
import "myheader.h";.. devrait être permis mais, en fait, ne l'est pas parce que les noms d'en-têtes ne sont pas permis dans la grammaire à cet endroit. On corrige cela. Ready, DR.
Amusant aussi. Examinez ceci :
void f(); // potentially throwing
bool b = noexcept(noexcept(f())); // ought to be "true"Dans un contexte non-évalué, il n'y aura pas d'exception alors b devrait être true mais on n'a pas de mots pour le dire. C'est un tout petit changement. Ready, DR.
On ne permet pas traditionnellement d'introduire des fonctions nommées (autres que les fonctions membres spéciales) dans un union anonyme, mais avec la réflexivité ça pourrait arriver. Il faut ajouter un peu de terminologie pour éviter cela. La proposition consiste en une série d'interdits.
Shafik Yaghmour demande si on pouvait observer les membres d'un union anonyme (il veut savoir si on provoque un changement de comportement et s'il faut une entrée dans l'annexe C). Hubert Tong explique qu'il y a une manière maudite de le faire à travers decltype(*this) et qu'il existe un Core Issue pour clore cette malédiction. On ne veut pas toucher à l'annexe C car il faudrait au passage dire qu'on ne sait pas ce qui se passe si quelqu'un fait ça en pratique.
Ready, DR.
On parle ici d'un problème de grammaire « pur et dur » avec la production grammaticale condition. On s'assure que le correctif fonctionne. Ready, DR.
On a une liste de candidats pour les conversions dans le langage, mais le texte ne traite pas correctement les cv-qualifiers. On corrige le texte pour indiquer qu'on ne tient pas compte de ces qualifications dans chacun de ces cas. Ça évite d'écrire deux opérateurs de conversion, on vers T et un vers const T par exemple, et de provoquer une ambiguïté.
On fait le tour ces cas un par un pour éviter de faire des bêtises. Ready, DR.
Avec la réflexivité on peut avoir des constant expressions de type void ce qui n'était pas permis auparavant. C'est pas dramatique mais le texte ne le permet pas.
Le changement à faire (ajouter if any au mot qui parle de la valeur de l'expression) est simple. On examine l'option de faire d'autres mini retouches du genre (des if not void ici et là) mais ça semble envahissant. Ready.
Le texte qu'on a semble parfois contradictoire quant à l'unicité (ou non) de chaque objet produit par std::meta::define_static_array(). On ajoute quelques mots ici et là pour clarifier l'intention. Ready.
L'enjeu est que les valeurs du littéral entier utilisé ne sont pas clairement délimitées. Les clarifications incluent interdiction des littéraux maison et interdiction des délimiteurs.
Le texte ne dit pas ce que ce terme signifie, et le définir semble susceptible d'alléger le texte et de le clarifier à plusieurs endroits.
Ready, DR.
On examine les changements apportés depuis hier. Tout semble convenable. On porte ça pour vote samedi.
On examine les changements les plus récents. Tout semble convenable. On porte ça pour vote samedi. DR.
Cette proposition tardive est écrite en réaction aux votes pris chez EWG sur le sujet plus tôt cette semaine. Elle se compose d'une reformulation de texte préexistant avec plusieurs suppressions et quelques ajouts, et nous devons examiner chacune de ces modifications pour éviter un bris.
On travaille le tout (il y a beaucoup de répétition, mais ça a été écrit dans des délais très restreints alors ça se comprend).
Jason Merrill et John Spicer se demandent si le texte décrit clairement ce qui est assujetti à SFINAE ou non, ce qui est l'un des principaux objectifs du texte (même si c'est pas évident pour les non-initié(e)s). On compare les cas où il faut instancier une lambda, une clause noexcept, un contrat, un paramètre par défaut d'une fonction, etc. pour voir si des cas particulier sont requis. C'est important : une erreur de spécification pourrait empêcher des programmes de compiler! En même temps, on veut quelque chose d'ici demain car beaucoup d'aspects importants du langage dépendent de cette définition.
On constate que les seules clauses noexcept qui doivent être traitées séparément sont celles sur une signature de fonction. On ajuste le texte en conséquence.
On a quelques cas qui peuvent sembler contradictoires sans une priorité claire, alors il faudra clarifier ceux-ci. On pense aussi qu'il peut manquer des trucs comme des classes locales à des function templates.
C'est un travail de près de deux heures qui nous amène à la fin de la journée. Brian Bi s'assure que l'on ait un accord sur les recommandations accumulées en vue de ce qui sera manifestement une mise à jour faite durant la nuit, et demande quelles sont les recommandation de CWG quant aux annotations car il devra retourner devant EWG demain pour les en informer.
(grosse journée!)
On discute de choses et d'autres, puis on commence à examiner le plan de match de la journée quand quelqu'un vient nous informer que EWG aimerait savoir ce qui se passe avec Core Issue 3112, mais il se trouve que c'est une banalité pour eux alors on les rassure.
Jens Maurer nous explique que la section constant expression du standard a été renommée en constant evaluation, section 7.7, et comprend désormais sept sous-sections. Ouf! Davis Herring a été une force importante derrière ce changement. Ça montre à quel point cet aspect du langage est devenu important au fil des derniers quinze ans. Quelqu'un signale que les gens impliqués dans la réflexivité souhaitent faire disparaître le terme immediate comme dans immediate function, mais ce ne sera pas pour C++26.
Il y a un problème profond qui se cache ici, mais à court terme ce qu'on peut faire est corriger la faute mineure (un C devient un CI).
Ceci concerne un problème découvert cette semaine :
template<class T>
constexpr int v(T x)
pre(x > 0)
{
return x; // #1
// return *T(); // #2
}
template<int> struct C;
template<class T>
constexpr int f(C<v(T())> *) { return 1; } // #3
template<class T>
constexpr int f(void*) { return 2; } // #4
static_assert(f<int>((void*)0) == 2);Joshua Berne pense que ça devrait être un hard error. Jens Maurer fait remarquer que le compilateur ne peut pas éviter d'instancier le cas problématique. John Spicer pense pour sa part que c'est un très gros problème. On débat d'approches à ce problème pendant près d'une heure; je crois comprendre qu'il serait possible de choisir un Build Mode de contrats qui ferait un diagnostic ici sans échouer la compilation, mais je dois avouer ne pas avoir joué avec les contrats suffisamment pour bien voir les contours de ce problème.
On accepte de fermer cette Core Issue en tant que NAD étant donné que cela reflète l'intention. Cela dit, les débats se poursuivent. Certains voient d'autres exemples qu'ils estiment plus représentatifs du problème, d'autres (par exemple Hubert Tong) pensent que LWG va vivre ce problème violemment avec les surchagres de swap().
(Jens Maurer fait remarquer que nous avons déjà 40 propositions en attente de traitement chez CWG pour C++29... Ouf!)
Le protocole get<I>() utilisé pour accéder au I-ème membre d'un tuple est mal spécifié dans le cas des prvalues si on est dans un contexte constexpr. La proposition est de faire en sorte que les Structured Bindings pour des prvalues n'utilisent pas de types références.
Nos débats font ressortir que T&& (les références de relais) et constexpr ont des interactions déplaisantes. On a probablement un problème plus profond qui se cache ici. Il se trouve que ceci :
constexpr auto [...Is] = std::make_index_sequence<2>();... est implémenté sous la forme :
constexpr auto e = std::integer_sequence<std::size_t, 0, 1>();
constexpr std::size_t && Is#0 = 0;
constexpr std::size_t && Is#1 = 1;... où les temporaires résultantes sont modifiables, ce qui n'est pas souhaitable. C'est un effet secondaire a priori insoupçonné de P2686 qui est pour l'essentiel utile. On va créer une Core Issue pour le problème de fond (que devrait-on déduire avec auto&& dans un constexte constexpr?) et on l'examinera un autre jour (c'est un problème qui aura beaucoup de répercussions).
Ready, mais pas un DR
La proposition change le sens d'un expansion statement sur un tableau même s'il n'est pas constant, ce qui est possible parce que la taille est connue à la compilation. Le résultat serait conceptuellement :
[&] consteval {
std::ptrdiff_t result = 0;
for (auto i = begin ; i != end ; ++i, ++result);
auto b = begin-expr ;
auto e = end-expr ;
for (; b != e; ++b) ++result;
return result; // distance from begin to end
}()On discute longtemps de l'impact de constexpr sur le code généré et du fardeau que cela peut représenter pour un usager. Jakub Jelinek remarque un bogue de spécification dans la proposition, bogue qu'on s'applique à corriger sur-le-champ.
(brève pause matinale; quelques amis dont c'est la première rencontre du comité se joignent à nous, il semble qu'on leur ait recommandé de « vivre l'expérience Core » qui est très différente de ce qui se passe dans les autres pièces)
Avec ce qui suit, chaque élément sera de type int& avec le texte existant alors que l'intention était d'avoir int&&, int& et int&& respectivement :
auto f() -> std::tuple<int, int&, int&&>
auto g() -> void {
template for (auto&& elem : f()) {
;
}
}La proposition est d'utiliser static_cast<decltype(ui)&&>(ui) pour chaque élément ui de elem qui n'est pas un lvalue (notez que ui est un Structured Binding dans le cas couvert par cette proposition). Ready, pas un DR.
La proposition met de l'avant que le texte n'empêche pas un Closure Type d'être final. Elle clarifie que ça ne doit pas être le cas. Ready, DR.
Dans le cas suivant :
struct A {
char c;
int i;
} a;
char d;... il y aura probablement du padding entre a.c et a.i. Rien n'empêche une implémentation d'y placer d, mais cela semble indésirable.
Davis Herring signale qu'on a une prohibition contre deux objets occupant des espaces disjoints à moins que ce ne soit pas observable (intro.object#10), accompagné d'une note non-normative. C'est pas une note très heureuse (intro.object#footnote-16). On essaie de voir si c'est suffisant ici. NAD
Ces opérateurs ne sont pas immediate-escalating avec le texte existant. La proposition est de corriger cela. C'est tout petit. Ready, DR.
On interdit les spécialisations de fonctions membres virtuelles non-consteval par des fonctions membres consteval, mais le texte ne tient pas compte de l'immediate escalation. La proposition est de clarifier la situation et inscrire cet interdit dans le texte. C'est tout petit. Ready, DR (mais qui mentionne un nouveau mécanisme). Il se peut que cet interdit disparaisse avec C++29, mais on verra une fois rendus là.
On a une divergence d'implémentation :
struct C {
C(int) = delete;
C(){};
};
decltype([b = C(3)](){ return 4; }()) x; //MSVC accepts; gcc and clang rejectEn pratique, c'est un bogue de MSVC (b n'est pas utilisé dans la lambda, mais il faudrait détecter le problème dans la capture malgré tout) mais la proposition améliore le texte du standard alors on l'examine. On retire une phrase un peu mal avisée et redondante, mais Ok pour le reste. Ready, DR.
Il manque un cas dans le texte existant. On remplace operator new par allocation function à quelques endroits, essentiellement. Ready, DR.
On parle d'une proposition de clarification du « moment » où se produit une constant evaluation. C'est un document de réflexion profonde sur la mécanique d'évaluation des expressions. On passe beaucoup de temps à lire, débattre du choix des mots, examiner si les tournures au pluriel sont trop obscures, etc.
(après une heure et plus de travail et de discussion, on prend une pause dîner; j'en profite pour faire une brève sieste)
(on poursuit sur D4143)
Le terme trial evaluation, utilisé informellement pour décrire certains aspects du fonctionnement des compilateurs, a été changé pour determination pour éviter de donner l'impression qu'il y ait une évaluation (ce n'est pas le cas)
Travailler sur ce genre de texte est très difficile, même pour les expert(e)s dans la salle. On essaie de mettre en place un modèle qui nous permettra d'en arriver à une forme de perspective partagée de ce qui se passe dans la machine abstraite de C++ quand on a un contexte constexpr, or la machine abstraite est un modèle d'exécution et ce qui se passe à la compilation y est opaque. On en vient au point où la partie qui offre un modèle de l'évaluation d'une expression constexpr et son passage à l'exécution est retiré malgré sa pertinence, mais parce que c'est trop difficile d'accès pour l'aspect pédagogique souhaité pour cette proposition.
On garde le reste de la proposition, et on ouvre une Core Issue pour la partie qui n'a pas reçu l'assentiment de la salle.
Brian Bi nous explique que EWG ne voulait pas trancher quant au moment où les annotations sont instanciées. Pour cette raison, la proposition a été dépouillée de toute référence aux annotations.
Un des questionnements que nous avons avec la proposition est le risque d'instancier plus de templates que l'on ne le voudrait. On essaie de considérer les cas décrits dans la proposition un à un pour voir ce qui se passera en pratique.
On a un nouveau terme grammatical, separately instantiated construct, pour couvrir les trucs qui sont instanciés séparément des classes ou des fonctions que l'on instancie et qui ne dépendent pas de celles-ci.
Il y a un malaise quant à certains aspects du texte proposé. En particulier, le immediate context est, en pratique, ce qui est fait lors d'une substitution dans un template, et certains sont d'avis que le texte sur la table ne dit pas tout à fait cela. Hubert Tong pose le problème sous la forme d'un template sur T* pour lequel on substituerait un type spécifique pour T et ce type serait une référence : le texte permet-il de dire qu'on forme un pointeur sur une référence?
On met ça de côté pour le moment, mais l'essentiel est bon.
(brève pause d'après-midi; les ami(e)s à Croydon parlent de la collation offerte là-bas, une sorte de gâteau chocolat-Guiness... Mioum!)
Au retour, Hubert Tong propose une formule alternative pour la définition du immediate context. On décide aussi d'aborder son questionnement pré-pause avec un exemple :
template<typename T>
T* fun(T && v); // #1: the deduction substitution locus is the function type « function
// of (rvalue reference to T) returning pointer to T »
void fun(...); // #2
void test() {
int i{};
fun(i); // selects #2 (forming the pointer to reference to int fails in #1)
}Joshua Berne propose aussi ceci :
template <typename T, typename U, typename V>
T* fun(T&& y, U&& u, V&& v)
noexcept( sizeof(U*) > 0)
pre(sizeof(V*) > 0);
int* fun(...);
int i;
int *j = fun(i,2,3); // OK
int *k = fun(1,i,3); // error
int *l = fun(1,2,i); // errorOn retravaille ensuite la définition du immediate context pour en clarifier la temporalité et la chronologie des instanciations.
Après plusieurs heures, c'est fait. Ready, on l'amène au vote demain.
Le texte qui décrit quelle fonction de déallocation sera utilisée pour une fonction d'allocation donnée lorsque le constructeur de l'objet échoue manquait d'amour. Entre autres, le texte était inadéquat pour les fonctions de déallocations supprimées. Avec la proposition, si aucune fonction de déallocation convenable n'est disponible, alors aucune n'est choisie tout simplement.
La proposition a besoin d'un peu de travail. On parle d'un processus de lookup mais il n'est pas décrit alors on cherche à spécifier la gestion du premier paramètre.
Avec la proposition, on aurait ceci :
struct A {};
struct T { T(); };
void* operator new(std::size_t s, A& al); // #1
template<int = 0>
void operator delete(void* p, A& al); // #2
A al;
T *p = new (al) T; // Uses #1 and #2.... et cela :
template<int I>
struct A {};
struct T { T(); };
void* operator new(std::size_t s, A<0>& al); // #1
template<int I>
void operator delete(void* p, A<I>& al);
void operator delete(void* p, A<0>& al);
A<0> al;
T *p = new (al) T; // Uses #1. No deallocation function is selected as two candidates remain
... de même que cela :
template<int I>
struct A {};
struct T { T(); };
void* operator new(std::size_t s, A<0>& al); // #1
template<int I> requires (I > 0)
void operator delete(void* p, A<I>& al);
void operator delete(void* p, A<0>& al); // #2
A<0> al;
T *p = new (al) T; // Uses #1 and #2.Après environ une heure de travail, on décide d'aller de l'avant. Ready, DR.
On revient sur l'exemple suivant :
constexpr const char *f() { return "foo"; }
constexpr bool b1 = "foo" == "foo"; // error: non-constant
constexpr bool b2 = f() == f(); // error: non-constant
constexpr const char *p = f();
constexpr bool b3 = p == p; // OK, value of b3 is true
constexpr bool b4 = "xfoo" + 1 == "foo\0y"; // error: non-constant; string literal object could contain "xfoo\0y"
constexpr bool b5 = "foo" == "bar"; // OK, value of b5 is false
constexpr bool b6 = "foo" == "oo"; // OK, value of b6 is false; offsets would be different in a merged string literal object
constexpr std::initializer_list<int *> il1 = { (int *)nullptr };
constexpr std::initializer_list<unsigned long> il2 = { 0 };
constexpr bool b7 = il1.begin() == (void *)il2.begin(); // error: non-constantOn nous fait remarquer que la comparaison de tableaux bruts avec == ne compile plus depuis C++26 (bonne nouvelle!) alors b1, b5 et b6 doivent être ajustés. CWG étant CWG, on décide de régler le problème de trois façons différentes (j'adore Core!) :
constexpr const char *f() { return "foo"; }
constexpr bool b1 = +"foo" == "foo"; // error: non-constant
constexpr bool b2 = f() == f(); // error: non-constant
constexpr const char *p = f();
constexpr bool b3 = p == p; // OK, value of b3 is true
constexpr bool b4 = "xfoo" + 1 == "foo\0y"; // error: non-constant; string literal object could contain "xfoo\0y"
constexpr bool b5 = "foo" == "bar" + 0; // OK, value of b5 is false
constexpr bool b6 = (const char*) "foo" == "oo"; // OK, value of b6 is false; offsets would be different in a merged string literal object
constexpr std::initializer_list<int *> il1 = { (int *)nullptr };
constexpr std::initializer_list<unsigned long> il2 = { 0 };
constexpr bool b7 = il1.begin() == (void *)il2.begin(); // error: non-constantOn achète. Ready.
La question est simple : ceci est-il bien formé?
float x = 1e100000000000000000000000000000000000000000000000.f;La résolution proposée mène à ceci, qui suppose que la plateforme support std::float32_t :
std::float32_t x = 0.0f32; // value 0 is exactly representable
std::float32_t y = 0.1f32; // rounded to one of two values nearest to 0.1
std::float32_t z = 1e1000000000f32; // either greatest finite value or positive infinityReady, DR.
Les dernières minutes de la semaine chez CWG permettent de discuter de trucs d'ordre administratif (horaire des téléconférences, présentation des Core Motions demain, ce genre de truc). On finit sur un truc amusant qui a été écrit aujourd'hui...
On a blagué là-dessus aujourd'hui, mais on a une note non-normative qui semble faire croire que la règle du as-if ne s'applique qu'à certains aspects du standard. On va supprimer ça.
On allait la regarder mais Brian Bi nous dit que le Wording n'est pas prêt. Ce sera pour une autre fois.
On a un passage qui parle du this parameter, ce qui semble un peu mêlant. Le terme implicit object est plus convenable. Ready, DR.
Les standards de C et C++ ont divergé pour les énumérateurs avec C23, car en C++ le type d'un énumérateur est le type énuméré alors qu'en C, c'est toujours un type entier :
enum e { A };
void f() {
auto x = A;
int *p = &x; // valid C, invalid C++
}
sizeof(A) == sizeof(int) // in C
sizeof(A) == sizeof(e) // in C++
/* and sizeof(int) is not necessarily equal to sizeof(e) */Ready, mais pas un DR.
Chaque annotation provoque quelque chose de nouveau dans notre grammaire, mais la question est de savoir si chaque instanciation d'un template annoté provoque une annotation distincte :
template<int> int x [[=1]];
template<int A> int y [[=A]];
static_assert(annotations_of(^^x<0>) == annotations_of(^^x<1>)); // #1
static_assert(annotations_of(^^y<0>) == annotations_of(^^y<1>)); // #2On avait déjà ceci, où f() a une annotation et g() en a cinq :
[[=1]] void f();
[[=2, =3, =2]] void g();
void g [[=4, =2]] ();... de même que ceci :
template<class T>
[[=T::type()]] void f(T t);
void f(int);
void g() {
f(0); // OK
f('0'); // error, ...
}On souhaite ajouter ceci :
template<int> int x [[=1]];
static_assert(annotations_of(^^x<0>) != annotations_of(^^x<1>)); // OKMalheureusement, le temps nous manque. Celle-ci ira à C++29.
Après quelques irritants audio, Nina Ranns nous accueille.
ABI Group : pas de rapport à offrir.
Admin Group : donne les chiffres pour la participation. Au total, 34 National Bodies cette semaine. Wow!
SG1: Concurrency and Parallelism Study Group : Olivier Giroux nous annonce que ce sera son dernier meeting après 15 ans de participation. Il relate que SG1 a débuté en 2008 avec Hans Boehm comme premier Chair. Olivier est entrée en 2011 et est Chair depuis 2018. Ruslan Arutyunyan sera le Chair désormais. Parmi les gros dossiers, après 15 ans, on pourra bientôt remplacer std::async() et offrir des seqlocks. Que dire des Fibers? Que retient-il? Faut accepter la rétroaction du groupe plutôt que la combattre. On cherche la « vision WG21 » de votre idée, elle cesse de vous appartenir (il pense à std::concurrent_queue entre autres). Olivier donne ensuite des conseils aux Chairs du futur de manière générale, en particulier sur la prise de votes pour accroître le consensus et pour éviter les votes qui mettront l'auteur ou le groupe dans le pétrin. Il offre ensuite une vision sur la culture des groupes, qui varie selon les groupes et est influencée par la personne jouant le rôle de Chair. Ruslan Arutyunyan promet ensuite de faire de son mieux pour continuer le travail entrepris par Olivier et rend hommage à ce dernier.
SG2: Modules Study Group : inactif cette semaine
SG3: File System Study Group : inactif cette semaine
SG4: Networking Study Group : inactif cette semaine
SG5: Transactional Memory : inactif cette semaine
SG6: Numerics : le groupe s'est rencontré la moitié de la semaine (une grosse semaine pour ce groupe!) avec une salle pleine et des participant(e)s à distance. Beaucoup de discussions sur les nombres à virgule flottante et les standards IEEE. Beaucoup de volontaires mais peu de propositions. On liste les dossiers sur lesquels on a travaillé.
SG7: Reflection : inactif cette semaine
SG8: Concepts : inactif cette semaine
SG9: Ranges : inactif cette semaine
SG10: Feature Test : inactif cette semaine
SG12: Undefined and Unspecified Behavior : inactif cette semaine
SG13: I/O : inactif cette semaine
SG14: Low Latency : inactif cette semaine, mais on travaille chaque mois sur la proposition de Patrice Roy et sur les exceptions pour systèmes embarqués. Les interactions avec Networking et les exécuteurs nous occuperont au cours des prochains mois
SG15: Tooling : quelques travaux cette semaine
SG16: Unicode : inactif cette semaine mais on progresse. Tom Honermann quitte le poste de Chair après sept ans et sera remplacé par Steve Downey. Il nous montre que SG16 a adopté un caractère Unicode, soit le caractère de remplacement!
SG17: EWG Incubator : inactif cette semaine
SG18: LEWG Incubator : deux après-midi cette semaine, et nous finissons nos rencontres à l'heure! Nous avons fini de traiter la bibliothèque Quantities and Units sur laquelle nous travaillons depuis Tokyo.
SG19: Machine Learning : inactif cette semaine, mais on travaille chaque mois. Nos gros sujets sont les statistiques et le Machine Learning. Nous travaillons dans une acception plus globale de C++ et de l'intelligence artificielle contemporaine, C++ étant le substrat sur lequel le reste du monde de l'IA contemporaine est construit.
SG20: Education : rencontre mardi après-midi. Les Teaching Guidelines s'en viennent. Nous avons discuté de l'adaptation de l'enseignement à l'avènement de l'IA. Nous avons discuté de l'écosystème qui complique l'accès à C++ pour les débutant(e)s
SG21: Contracts : inactif cette semaine
SG22: C/C++ Liason : inactif cette semaine
SG23: Safety and Security : nous nous sommes rencontrés mercredi matin et nous avons traité deux propositions (il y en avait quatre mais l'auteur des deux autres n'était pas présent). Nous planifions être la force derrière le développement des Profiles.
EWG : grosse semaine, traitant toutes les propositions pour C++26 (une seule n'eut pas de consensus), incluant 13 dossier impliquant une interaction avec CWG. On garde les contrats! On a abordé 27 propositions pour C++29. L'après-midi de vendredi (quatre heures) fut consacré au Memory Safety, et il y avait des gens debout dans la salle, incluant 83 personnes votantes.
LEWG : on fait la (longue!) litanie des dossiers traités, et il y en a plusieurs dizaines. Le groupe a traité 248 LEWG Issues, c'était une grosse semaine! On met de l'avant l'importance de la collaboration avec LWG.
CWG : on a traité tous les NB Comments sur la table, à travers plusieurs papiers. Quelques votes désignés DR à Kona l'ont été incorrectement et nous aimerions corriger cette situation aujourd'hui. La Core Issue 3158 sera traitée séparément des autres (vote 8) à la demande de certaines personnes dans la salle. Nous avons traité 412 NB Comments au total, incluant lors des téléconférences.
LWG : on a travaillé très fort et on n'a pu traiter que des dossiers pour C++26, rien pour C++29. Au mieux de ma connaissance nous avons traité tous les NB Comments pour C++26. On a peut-être battu notre record pour le nombre de votes à tenir. Au total, on soumet environ cent changements pour votre approbation cette semaine.
Les votes amenés par CWG cette semaine sont les suivants.
1. Correct the categorization of core issue 3081 to not be a Defect Report and of core issues 3082 and 3110 to be Defect Reports. (The resolutions of all three core issues were adopted at the November, 2025 WG21 meeting.)
Adopté
2. Accept as Defect Reports and apply the proposed resolutions of all issues except issues 3088, 3119, 3122, 3123, 3124, 3131, 3135, 3140, 3141, 3143, 3145, 3149, 3162, and 3172 in P4160R0 (Core Language Working Group "ready" Issues for the March, 2026 meeting) to the C++ Working Paper.
Adopté
3. Apply the proposed resolutions of issues 3088, 3119, 3122, 3123, 3124, 3131, 3135, 3140,
3141, 3143, 3145, 3149, 3162, and 3172 in P4160R0 (Core Language Working Group "ready" Issues for the March, 2026 meeting) to the C++ Working Paper.
Vote demandé. C'est rare pour ce genre de vote alors on demande laquelle ou lesquelles des Core Issues pose problème pour scinder le vote et traiter ce qui irrite séparément. On nous indique que 3141 brisera du code existant.
Avec cette scission (ajout du vote 13, plus bas), pas de vote. Adopté.
4. Accept as a Defect Report and apply the changes in P3924R1 (Fix inappropriate font choices for "declaration") to the C++ Working Paper. This addresses ballot comment US 11-400.
Adopté
5. Accept as a Defect Report and apply the changes in P4136R2 (#line is not in line with existing implementation) to the C++ Working Paper. This addresses ballot comment FR-009-108.
Adopté
6. Accept as a Defect Report and apply the changes in P4004R1 (Reconsider CWG 1395 "Partial ordering of variadic templates reconsidered") to the C++ Working Paper.
Adopté
7. Accept as a Defect Report and apply the changes in P3865R3 (Class template argument deduction (CTAD) for type template template parameters) to the C++ Working Paper.
Adopté
8. Apply the changes in P3598R0 (CWG 3158 — const-ification of Splice Expressions) to the C++ Working Paper.
Vote demandé. Consensus pour, mais quelques noms importants contre. Intéressant
9. Apply the changes in P3726R2 (Adjustments to Union Lifetime Rules) to the C++ Working Paper.
On signale que cela adresse DE 087. On n'a pas à changer le libellé, mais on le fait
Adopté
10. Accept as a Defect Report and apply the changes in P4143R0 (Constant evaluation when?) to the C++ Working Paper. This partially addresses ballot comment US 33-065.
Adopté
11. Accept as a Defect Report and apply the changes in P4149R1 (Define "immediate context") to the C++ Working Paper. This addresses ballot comment US 54-100 and core issues 1844 and 2296.
Vote demandé. Consensus pour
12. Accept as a Defect Report and apply the changes in P3769R1 (Clarification of placement new deallocation) to the C++ Working Paper.
Adopté
13. Apply the proposed resolutions of issue 3141 in P4160R0 (Core Language Working Group "ready" Issues for the March, 2026 meeting) to the C++ Working Paper.
Vote demandé. Consensus pour
Les votes amenés par LWG cette semaine sont les suivants. Il y avait une erreur dans le libellé du vote 37 mais la proposition, elle, est correcte.
1. Apply the changes in P4145R0 (C++ Standard Library Ready Issues to be moved in Croydon, Mar. 2026) to the C++ working paper.
Adopté
2. Apply the changes in P4146R0 (C++ Standard Library Immediate Issues to be moved in Croydon, Mar. 2026) to the C++ working paper.
Adopté
3. Apply the changes in P3690R1 (Consistency fix: Make simd reductions SIMD-generic) to the C++ working paper. This addresses ballot comment AT8-279.
Adopté
4. Apply the changes in P3844R4 (Reword [simd.math] for consteval conversions) to the C++ working paper. This addresses ballot comment DE-286.
Adopté
5. Apply the changes in P3932R0 (Fix LWG4470: Fix integer-from in [simd]) to the C++ working paper.
Adopté
6. Apply the changes in P4012R1 (Value-preserving consteval broadcast to simd::vec) to the C++ working paper. This addresses ballot comment DE-286.
Adopté
7. Apply the changes in P3886R0 (Wording for AT1-057) to the C++ working paper. This addresses ballot comment AT 1-057.
Adopté
8. Apply the changes in P3936R1 (Safer atomic_ref::address (FR-030-310)) to the C++ working paper and update the value of the __cpp_lib_atomic_ref macro. This addresses ballot comment FR-030-310.
Adopté
9. Apply the changes in P4140R0 (Proposed resolution for US70-126: allow incomplete types in type_order) to the C++ working paper. This addresses ballot comment US 70-126.
Adopté
10. Apply the changes in P3373R4 (Of Operation States and Their Lifetimes) to the C++ working paper. CA-338.
Adopté
11. Apply the changes in P3986R1 (A Wording Strategy for Inlinable Receivers) to the C++ working paper. This addresses ballot comment CA-318.
Adopté
12. Accept as a Defect Report and apply the changes in P3059R2 (Making user-defined constructors of view iterators/sentinels private) to the C++ working paper. This addresses ballot comment GB 09-257.
Adopté
13. Accept as a Defect Report and apply the changes in P3725R3 (Filter View Extensions for Safer Use, Rev 3) to the C++ working paper. This addresses ballot comments AT9-249, RU-250, DE-251.
Adopté
14. Apply the changes in P3828R1 (Rename the to_input view to as_input) to the C++ working paper. This addresses ballot comment DE-248.
Vote demandé. Consensus pour
15. Apply the changes in P3795R2 (Miscellaneous Reflection Cleanup) to the C++ working paper. This addresses ballot comments US 42-078 and US 85-150 and US 122-184 and US 128-192 and US 95-202 and US 131-195.
Adopté
16. Apply the changes in P3948R1 (constant_wrapper is the only tool needed for passing constant expressions via function arguments) to the C++ working paper. This addresses ballot comments FR-019-210 and FR-021-218.
Adopté
17. Apply the changes in P3978R3 (constant_wrapper should unwrap on call and subscript) to the C++ working paper.
Adopté
18. Apply the changes in P3961R1 (Less double indirection in function_ref) to the C++ working paper. This addresses ballot comment RU-220.
Adopté
19. Apply the changes in P3981R2 (Better return types in std::inplace_vector and std::exception_ptr_cast) to the C++ working paper. This addresses ballot comments PL-006 and US 68-122 and US 150-228 and GB 08-225.
Quelqu'un demande de résumer l'intention brièvement. On procède
Adopté
20. Apply the changes in P4022R0 (Remove try_append_range from inplace_vector for now) to the C++ working paper. This addresses ballot comment PL-006.
Adopté
21. Apply the changes in P4037R1 (Supporting signed char and unsigned char in random number generation) to the C++ working paper. This addresses ballot comment RU-272.
Adopté
22. Apply the changes in P3450R1 (Extend std::is_within_lifetime) to the C++ working paper. This addresses ballot comment US 82-145.
Adopté
23. Apply the changes in P3982R2 (Split strided_slice into extent_slice and range_slice for C++26) to the C++ working paper. This addresses ballot comment PL-007.
Adopté
24. Apply the changes in P4144R1 (Remove span’s initializer_list constructor for C++26) to the C++ working paper.
Adopté (je m'attendais à de la résistance ici)
(brève pause)
25. Apply the changes in P3804R2 (Iterating on parallel_scheduler) to the C++ working paper. This addresses ballot comment RO 4-395.
Adopté
26. Apply the changes in P3787R2 (Adjoints to "Enabling list-initialization for algorithms": uninitialized_fill) to the C++ working paper. This addresses ballot comment FR-027-267.
Adopté
27. Apply the changes in P3842R2 (A conservative fix for constexpr uncaught_exceptions() and current_exception()) to the C++ working paper. This addresses ballot comments PL-012 and GB 03-119 and DE-120 and US 67-118 and FI-121.
Adopté
28. Apply the changes in P3826R5 (Fix Sender Algorithm Customization) to the C++ working paper. This addresses ballot comments US 207-328 and US 202-326 and FR-031-219 and FI-331 and CA-358.
Adopté
29. Apply the changes in P3980R1 (Task’s Allocator Use) to the C++ working paper. This addresses ballot comments US 254-385 and US 253-386 and US 255-384 and US 261-391.
Adopté
30. Apply the changes in P4156R0 (Rename meta::has_ellipsis_parameter to meta::is_vararg_function) to the C++ working paper. This addresses ballot comment FR-017-155.
On signale que la décision de soumettre ceci au vote a été prise tardivement hier et que certains sont préoccupés (on nous dit que « la maison n'est pas en feu », mais qu'il y a des préoccupations). L'enjeu est le suffixe _function qui diverge un peu de ce qui est représenté en termes de Core Wording.
Vote demandé. Consensus pour
31. Apply the changes in P3953R3 (Rename std::runtime_format) to the C++ working paper.
Adopté
32. Apply the changes in P4052R0 (Renaming saturation arithmetic functions) to the C++ working paper. This addresses ballot comment FR-026-265.
Adopté
33. Apply the changes in P3941R4 (Scheduler Affinity) to the C++ working paper. This addresses ballot comments US 232-366 and US 233-365 and US 234-364 and US 235-363 and US 236-362.
Adopté
34. Apply the changes in P3856R8 (New reflection metafunction - is_structural_type) to the C++ working paper. This addresses ballot comment US 49-090.
Adopté
35. Apply the changes in P3927R2 (task_scheduler support for parallel bulk execution) to the C++ working paper. This addresses ballot comment US 238-368.
Adopté
36. Apply the changes in P4151R1 (Rename affine_on) to the C++ working paper.
Adopté
37. Apply the changes in P4159R0 (Make
sender_in sender_to and receiver_of exposition-only) to the C++ working paper.
Adopté
38. Apply the changes in P4154R0 (Renaming various execution things) to the C++ working paper. This addresses ballot comments US 205-320 and RO 4-395.
Adopté
Les votes pour WG21 sont les suivants.
1. Adopt P1000R7 "Proposed C++ IS schedule".
Adopté (c'est le même modèle qu'à l'habitude, cycle de trois ans). On accepte avec amendements mineurs (il y avait des petites erreurs cléricales)
On commence officiellement le travail sur C++29!
2. Direct the Convener to transmit paper N5036 “ISO/IEC JTC1/SC22/WG21 White Paper, Extensions to C++ for Transactional Memory Version 2” for publication as an SC 22 white paper.
On explique qu'on a déjà voté ceci, mais que rien ne s'est passé chez SC22 parce que notre intention n'était pas claire à leurs yeux. On veut être limpides
Vote demandé. Consensus pour
3. Appoint a review committee composed of Daniel Krügler, Jonathan Wakely, Neelofer Banglawala, and Shafik Yaghmour to approve the correctness of the C++ working paper as modified by the polls approved at this meeting, and direct the Convener to transmit the approved updated working paper for DIS ballot.
Un représentant de pays énonce son opposition au standard sous sa forme actuelle, signalant que les contrats lui semblent dommageables pour le langage.
Quelqu'un signale que la formulation du vote ne dit pas clairement que les éditeurs pourront faire des changements. On nous rappelle que c'est un libellé standard, le même que d'habitude
Vote demandé. Consensus pour. C++26 est complet!
On prend la photo de groupe, participants Zoom inclus. J'ai notre Conure, Patapon, sur mon épaule (il a participé toute la semaine)

Direction group : c'est un groupe avec direction rotative. Daveed Vandevoorde sera le Chair pour la prochaine année. Il présente les grandes lignes des efforts attendus pour C++29 et pour la prochaine décennie.
Les prochaines rencontres seront à Brno (Czechie), N5011, le 8 juin 2026, puis à Búzios (Rio de Janeiro, Brésil), N5021, le 16 novembre 2026
On remercie les Chairs qui font un travail colossal. On gère les derniers dossiers administratifs (transport de l'équipement et tout), puis un ferme les livres vers 7 h 20 heure de Montréal (12 h 20 à Londres). Enfin, on félicite la nouvelle équipe adminsitrative qui a fait un excellent travail toute la semaine durant.