Skip to content

Commit 88266c2

Browse files
committed
Structured binding pack
1 parent c49bd33 commit 88266c2

File tree

1 file changed

+176
-2
lines changed

1 file changed

+176
-2
lines changed

_articles/c++_auto.md

Lines changed: 176 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,11 +1049,185 @@ function(auto{expr});
10491049

10501050
## Structured binding pack (depuis C++26)
10511051

1052-
Dans la continuité des [structured binding declaration](#structured-binding-declaration-depuis-c17), le C++26 ajoute la possibilité de d'extraire des éléments dans un [pack](/articles/c++/templates#pack) ([proposal](https://wg21.link/P1061R10), [approval](https://wg21.link/P1061R9/status)).
1052+
Dans la continuité des [structured binding declaration](#structured-binding-declaration-depuis-c17), le C++26 ajoute la possibilité de d'extraire des éléments d'un [pack](/articles/c++/templates#pack) ([proposal](https://wg21.link/P1061R10), [approval](https://wg21.link/P1061R9/status)).
10531053

10541054
Cette fonctionnalité n'est [pas encore supportée par les compilateurs](https://en.cppreference.com/w/cpp/26) à l'heure où j'écris.
1055+
On peut cependant la trouver en experimental sur Clang.
10551056

1056-
{% wip %}
1057+
Ce n'est pas une nouvelle fonctionnalité à proprement parler, il s'agit en fait d'une extension des [structured binding declaration](#structured-binding-declaration-depuis-c17)** leur permettant de supporter les [pack](/articles/c++/templates#pack).
1058+
1059+
{% highlight cpp linenos highlight_lines="4" %}
1060+
auto container = std::tuple{1, 2, 3};
1061+
1062+
auto [x, y, z] = container;
1063+
auto [...values] = container; // values contient les valeurs 1, 2 et 3
1064+
{% endhighlight %}
1065+
1066+
Pour [rappel](/articles/c++/templates#pack), un pack est un outil de **metaprogrammation** fonctionnant dans le **contexte d'une template**.<br>
1067+
Les *structured binding pack* sont donc utilisables **uniquement dans des fonctions templatées** (dans une "templated region"):
1068+
1069+
{% highlight cpp linenos highlight_lines="6" %}
1070+
auto main() -> int
1071+
{
1072+
auto container = std::tuple{1, 2, 3};
1073+
1074+
[[maybe_unused]] auto [x, y, z] = container; // Ok
1075+
[[maybe_unused]] auto [...values] = container; // error: pack declaration outside of template
1076+
}
1077+
{% endhighlight %}
1078+
1079+
Dans le contexte d'une template:
1080+
{% highlight cpp linenos highlight_lines="1" %}
1081+
template<class Container>
1082+
auto function(Container container) -> void
1083+
{
1084+
[[maybe_unused]] auto [...values] = container; // Ok
1085+
}
1086+
{% endhighlight %}
1087+
1088+
Ou écrit plus simplement:
1089+
{% highlight cpp linenos %}
1090+
auto function(auto container) -> void // Fonction template (le type du paramètre container est templaté)
1091+
{
1092+
[[maybe_unused]] auto [...values] = container; // Ok
1093+
}
1094+
{% endhighlight %}
1095+
1096+
> Ce sujet a levé beaucoup d'interrogations dans la [revision 9 du proposal](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p1061r10.html#removing-packs-outside-of-templates) pour rendre les *structured binding pack* utilisables **dans des fonctions non templatées**. Impliquant qu'une notion d'"**implicit template region**" soit ajoutée au langage pour les supporter.
1097+
>
1098+
> Il a été décidé dans la revision 10 que la notion d'"implicit template region" serait **trop complexe à implémenter** dans les compilateurs et ne serait donc **pas ajouté**, limitant l'usage des *structured binding pack* aux contextes de templates.
1099+
>
1100+
> Petite anecdote: [Jason Rice a implémenté cette notion d'"implicit template region" dans le compilateur Clang](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p1061r10.html#implementation-experience) avant que cette discussion ai lieu. Il a ensuite dû revenir en arrière et supprimer cette notion, disant que "Honnêtement, les contextes implicites de template ont grandement simplifié les choses, et une grande partie du code a été supprimée".
1101+
1102+
Pour poursuivre sur les différents usages de cette fonctionnalité, le pack n'est pas obligé de contenir tous les éléments du conteneur. Il est possible d'en décomposer quelques-uns avant ou après celui-ci.
1103+
1104+
{% highlight cpp linenos %}
1105+
auto container = std::tuple{1, 2, 3};
1106+
1107+
auto [x, ...others] = container; // Ok: x contient 1; Et others contient 2 et 3
1108+
auto [...others, z] = container; // Ok: z contient 3; Et others contient 1 et 2
1109+
{% endhighlight %}
1110+
1111+
Le pack peut également être vide si tous les éléments sont déjà décomposés dans des variables à part.
1112+
{% highlight cpp linenos %}
1113+
auto container = std::tuple{1, 2, 3};
1114+
1115+
auto [x, y, z, ...others] = container; // Ok: others est vide
1116+
auto [x, y, z, w, ...others] = container; // error: structured binding size is too small
1117+
{% endhighlight %}
1118+
1119+
En revanche il ne peut pas y avoir plusieurs packs dans la même *structured binding declaration*.
1120+
{% highlight cpp %}
1121+
auto [...pack1, ...pack2] = container; // error: multiple packs in structured binding declaration
1122+
{% endhighlight %}
1123+
1124+
### Exemples
1125+
1126+
Il n'est parfois pas très clair des possibilités qu'apporte qu'une telle fonctionnalité, c'est pourquoi je vous présente quelques exemples d'utilisation:
1127+
1128+
Une fonction ``print`` qui affiche une liste de valeurs de types variés:
1129+
{% highlight cpp linenos highlight_lines="3" %}
1130+
auto print(const auto& tuple) -> void
1131+
{
1132+
const auto& [...values] = tuple;
1133+
(std::cout << ... << values) << "\n";
1134+
}
1135+
1136+
auto main() -> int
1137+
{
1138+
auto tuple = std::make_tuple(1, 2, 3, "soleil");
1139+
print(tuple);
1140+
}
1141+
{% endhighlight %}
1142+
1143+
{% highlight console %}
1144+
123soleil
1145+
{% endhighlight %}
1146+
1147+
> Ceci est un exemple, privilégiez la fonction [``std::print``](https://en.cppreference.com/w/cpp/io/print) dans vos projets.
1148+
1149+
Une fonction de print un peu plus poussée qui affiche les variables membres d'une structure passée en paramètre:
1150+
{% highlight cpp linenos highlight_lines="10" %}
1151+
struct Data
1152+
{
1153+
int id;
1154+
double number;
1155+
std::string name;
1156+
};
1157+
1158+
auto printFields(const auto& object, const auto& tupleLike) -> void
1159+
{
1160+
const auto& [...fields] = tupleLike;
1161+
std::cout << "Champs:\n";
1162+
([&] { std::cout << object.*fields << '\n'; }(), ...);
1163+
}
1164+
1165+
auto main() -> int
1166+
{
1167+
auto data = Data{42, 3.14, "example"};
1168+
auto fields = std::make_tuple(&Data::id, &Data::number, &Data::name);
1169+
printFields(data, fields);
1170+
}
1171+
{% endhighlight %}
1172+
1173+
{% highlight console %}
1174+
Champs:
1175+
42
1176+
3.14
1177+
example
1178+
{% endhighlight %}
1179+
1180+
Une fonction qui appelle une fonction membre avec une liste d'arguments définis:
1181+
{% highlight cpp linenos highlight_lines="11" %}
1182+
struct Worker
1183+
{
1184+
auto doWork(int id, const std::string& task) -> void
1185+
{
1186+
std::cout << "Worker " << id << " is performing task: " << task << '\n';
1187+
}
1188+
};
1189+
1190+
auto invokeMemberFunction(auto func, const auto& tupleLike) -> void
1191+
{
1192+
const auto& [thisPointer, ...arguments] = tupleLike;
1193+
std::invoke(func, thisPointer, arguments...);
1194+
}
1195+
1196+
auto main() -> int
1197+
{
1198+
auto worker = Worker{};
1199+
auto taskInfo = std::make_tuple(&worker, 101, "Compile code");
1200+
invokeMemberFunction(&Worker::doWork, taskInfo);
1201+
}
1202+
{% endhighlight %}
1203+
1204+
{% highlight console %}
1205+
Worker 101 is performing task: Compile code
1206+
{% endhighlight %}
1207+
1208+
> Ceci est un exemple, privilégiez la fonction [std::apply](https://en.cppreference.com/w/cpp/utility/apply) dans vos projets.
1209+
1210+
Une fonction qui transforme une structure en tuple:
1211+
{% highlight cpp %}
1212+
struct Data
1213+
{
1214+
int id;
1215+
double number;
1216+
std::string name;
1217+
};
1218+
1219+
[[nodiscard]] auto toTuple(auto& object) -> auto
1220+
{
1221+
auto& [...values] = object;
1222+
return std::tie(values...);
1223+
}
1224+
1225+
auto main() -> int
1226+
{
1227+
auto data = Data{42, 3.14, "example"};
1228+
[[maybe_unused]] auto tuple = toTuple(data);
1229+
}
1230+
{% endhighlight %}
10571231

10581232
---
10591233

0 commit comments

Comments
 (0)