Skip to content

Commit 42ee066

Browse files
committed
C++ Auto : auto cast
1 parent 08e4f5a commit 42ee066

4 files changed

Lines changed: 122 additions & 15 deletions

File tree

_articles/c++_auto.md

Lines changed: 119 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -254,20 +254,17 @@ auto variable = MyClass{"Hello"}; // Ou avec l'uniform initialization
254254

255255
### Most vexing parse
256256

257-
A votre avis, quel est le type de ``number`` dans le code suivant ?
258-
257+
Quel est le type de ``number`` dans le code suivant ?
259258
{% highlight cpp highlight_lines="3" %}
260259
void function()
261260
{
262261
int number();
263262
}
264263
{% endhighlight %}
265264

266-
Une variable de type ``int`` vous dites ? Perdu!<br>
267-
C'est une fonction qui ne prend aucun argument et qui retourne un ``int``.
265+
Une variable de type ``int`` ? Non, c'est une fonction qui ne prend aucun argument et qui retourne un ``int``.
268266

269267
Et ici, quel est le type de ``foo`` ?
270-
271268
{% highlight cpp highlight_lines="3" %}
272269
void function(double number)
273270
{
@@ -277,14 +274,14 @@ void function(double number)
277274

278275
Est-ce un nombre de type ``int``, initialisé en lui fournissant ``number`` casté en ``int`` (avec ``int(number)``) ?
279276

280-
C'est une fonction ayant pour signature ``int foo(int);``.<br>
277+
Non, c'est une fonction ayant pour signature ``int foo(int);``.<br>
281278

282279
> Le langage C **autorise les parenthèses superflues autour des paramètres** des fonctions.
283280
{: .block-warning }
284281

285282
En réalité nous sommes ici dans une situation d'**ambigüité** entre **deux manières différentes** d'interpréter une définition (**variable** ou **fonction**).
286283

287-
Face à cette ambigüité, **le compilateur choisi toujours de considérer comme des fonctions** si ça peut l'être.
284+
Face à cette ambigüité, **le compilateur choisi toujours de considérer ces déclarations comme étant des fonctions**.
288285

289286
> Si les warnings (``-Wvexing-parse``) sont activés sur votre compilateur, celui-ci devrait être assez explicite quant à la raison de cette ambigüité.
290287
@@ -1384,9 +1381,21 @@ struct HeterogenousValueList {};
13841381
using MyList = HeterogenousValueList<42, 'X', 13u>;
13851382
{% endhighlight %}
13861383

1387-
### ``auto`` in template parameters (depuis C++20)
1384+
### ``auto`` in lambda template parameters (depuis C++20)
1385+
1386+
Avec le support des templates sur les lambdas en C++20, il est possible d'utiliser ``auto`` en type templaté.
1387+
1388+
{% highlight cpp linenos highlight_lines="1 8 9" %}
1389+
auto apply = []<auto Operation>(int lhs, int rhs) {
1390+
return Operation(lhs, rhs);
1391+
};
1392+
1393+
auto add = [](int lhs, int rhs) { return lhs + rhs; };
1394+
auto multiply = [](int lhs, int rhs) { return lhs * rhs; };
13881395

1389-
{% wip %}
1396+
std::println("3 + 4 = {}", apply.operator()<add>(3, 4)); // 7
1397+
std::println("3 * 4 = {}", apply.operator()<multiply>(3, 4)); // 12
1398+
{% endhighlight %}
13901399

13911400
## AA (Always Auto) (depuis C++17)
13921401

@@ -1456,16 +1465,114 @@ void sum(Lhs lhs, Rhs rhs);
14561465

14571466
## auto cast (depuis C++23)
14581467

1459-
Une manière générique d'obtenir la copie d'un objet en C++ est ``auto variable = x;``, mais une telle copie est une [lvalue](/articles/c++/value_categories#lvalue).
1468+
Une manière **générique** d'obtenir la copie d'un objet en C++ est ``auto variable = x;``, mais une telle copie est une [lvalue](/articles/c++/value_categories#lvalue).
14601469

1461-
``auto(a)`` (ou ``auto{x}``) permet d'en obtenir une copie sous forme de [prvalue](/articles/c++/value_categories#prvalue), ce qui peut être utile pour transmettre cet objet en paramètre à une fonction.
1470+
``auto(a)`` (ou ``auto{x}``) permet d'obtenir une copie sous forme de [prvalue](/articles/c++/value_categories#prvalue), ce qui peut être utile pour transmettre cet objet en paramètre à une fonction.
14621471

14631472
{% highlight cpp %}
14641473
function(auto(expr));
14651474
function(auto{expr});
14661475
{% endhighlight %}
14671476

1468-
{% wip %}
1477+
Cette écriture permet de dire **explicitement** au compilateur de faire une copie.<br>
1478+
Mais c'est également un **outil sémantique pour signifier aux développeurs de l'intention de faire une copie**.
1479+
1480+
Cela revient à écrire:
1481+
{% highlight cpp %}
1482+
auto copy(const auto& value)
1483+
{
1484+
return value;
1485+
}
1486+
{% endhighlight %}
1487+
1488+
{% highlight cpp %}
1489+
function(copy(expr));
1490+
{% endhighlight %}
1491+
Mais avec une syntaxe intégrée au langage.
1492+
1493+
> En réalité, ``auto(expr)``/``auto{expr}`` correspond plutôt à faire un ``std::decay_t<decltype(expr)>{expr}``, et non à appeler une fonction ``copy``.
1494+
1495+
> L'équivalence avec [``std::decay_t``](https://en.cppreference.com/w/cpp/types/decay) signifie également que les tableaux (ex: ``int[N]``) sont convertis en pointeurs (ex: ``int*``). Pour préserver le type tableau, il faut utiliser [``auto`` (Placeholder type specifiers)](#placeholder-type-specifiers-depuis-c11) ou [``decltype(auto)``](#decltypeauto-depuis-c14).
1496+
{: .block-warning }
1497+
1498+
{% highlight cpp %}
1499+
function(auto{expr});
1500+
{% endhighlight %}
1501+
équivaut à:
1502+
{% highlight cpp %}
1503+
function(std::decay_t<decltype(expr)>{expr});
1504+
{% endhighlight %}
1505+
1506+
A première vue on pourrait penser que ça ne répond à aucun besoin réel.<br>
1507+
Mais regardons [la motivation](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0849r8.html#Motivation) derrière cet ajout:
1508+
1509+
Prenons le code suivant, dans lequel on veut supprimer toute occurrence de la première valeur (``"A"``):
1510+
{% highlight cpp highlight_lines="3" %}
1511+
void erase_all_of_first(auto& container)
1512+
{
1513+
std::erase(container, container.front());
1514+
}
1515+
1516+
int main()
1517+
{
1518+
auto values = std::vector<std::string>{"A", "A", "B", "C", "D", "B", "B"};
1519+
erase_all_of_first(values);
1520+
std::println("{}", values);
1521+
}
1522+
{% endhighlight %}
1523+
1524+
{% highlight console %}
1525+
["B", "C", "D"]
1526+
{% endhighlight %}
1527+
Les deux occurrences de ``"A"`` ont bien été supprimées, mais où sont passées les autres occurrences de ``"B"`` ?
1528+
1529+
Regardons comment fonctionne ``std::erase``. La fonction ``std::erase`` se présente comme suit:
1530+
{% highlight cpp %}
1531+
template<class T, class Alloc, class U>
1532+
constexpr typename std::vector<T, Alloc>::size_type erase(std::vector<T, Alloc>& c, const U& value);
1533+
{% endhighlight %}
1534+
1535+
Elle prend une **référence** sur un ``std::vector`` ainsi qu'une **référence constante** sur l'élément à rechercher et **à supprimer** dans le conteneur.
1536+
1537+
[La documentation](https://en.cppreference.com/w/cpp/container/vector/erase2) nous dit que la fonction ``std::erase`` supprime chaque élément du conteneur ``c`` égal à l'argument ``value`` de la manière suivante:
1538+
{% highlight cpp %}
1539+
auto it = std::remove(c.begin(), c.end(), value);
1540+
auto r = c.end() - it;
1541+
c.erase(it, c.end());
1542+
return r;
1543+
{% endhighlight %}
1544+
Hors, ``std::remove`` ne supprime pas réellement d'éléments, mais [les réorganise](https://en.cppreference.com/w/cpp/algorithm/remove#Possible_implementation): il déplace vers le début du conteneur les éléments à conserver, **en écrasant les éléments à supprimer** par ces affectations.
1545+
1546+
Cela signifie que ``container.front()``, passé par référence constante à ``std::erase``, **peut être modifié pendant l'appel**, notamment si sa position est réutilisée pour stocker une autre valeur (comme ``"B"`` dans l'exemple).
1547+
1548+
Résultat: la valeur utilisée dans la comparaison **est modifiée en cours de traitement** et **les suppressions deviennent incohérentes**.
1549+
1550+
Pour éviter cela, il faut s'assurer que la valeur passée à ``std::erase`` reste **indépendante** du conteneur pour éviter les effets de bord:
1551+
{% highlight cpp highlight_lines="3" %}
1552+
void erase_all_of_first(auto& container)
1553+
std::erase(container, auto{container.front()});
1554+
{% endhighlight %}
1555+
1556+
Ce qui nous donne:
1557+
{% highlight cpp highlight_lines="3" %}
1558+
void erase_all_of_first(auto& container)
1559+
{
1560+
std::erase(container, auto{container.front()});
1561+
}
1562+
1563+
int main()
1564+
{
1565+
auto values = std::vector<std::string>{"A", "A", "B", "C", "D", "B", "B"};
1566+
erase_all_of_first(values);
1567+
std::println("{}", values);
1568+
}
1569+
{% endhighlight %}
1570+
1571+
{% highlight console %}
1572+
["B", "C", "D", "B", "B"]
1573+
{% endhighlight %}
1574+
1575+
``auto(expr)``/``auto{expr}`` a été proposé pour résoudre des situations comme celle-ci.
14691576

14701577
## Structured binding pack (depuis C++26)
14711578

articles_c++.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<div class="container">
1212
<div class="text-center">
1313
<span class="rounded-card sticky-top">
14-
<a href="/articles/{{page.category}}" class="button nier highlight-hover bullet triangle-left">Back</a>
14+
<a href="/articles" class="button nier highlight-hover bullet triangle-left">Back</a>
1515
</span>
1616
</div>
1717
{% assign articles = site.articles | where: "category", "c++" %}

articles_css.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<div class="container">
1212
<div class="text-center">
1313
<span class="rounded-card sticky-top">
14-
<a href="/articles/{{page.category}}" class="button nier highlight-hover bullet triangle-left">Back</a>
14+
<a href="/articles" class="button nier highlight-hover bullet triangle-left">Back</a>
1515
</span>
1616
</div>
1717
{% assign articles = site.articles | where: "category", "css" %}

articles_git.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<div class="container">
1212
<div class="text-center">
1313
<span class="rounded-card sticky-top">
14-
<a href="/articles/{{page.category}}" class="button nier highlight-hover bullet triangle-left">Back</a>
14+
<a href="/articles" class="button nier highlight-hover bullet triangle-left">Back</a>
1515
</span>
1616
</div>
1717
{% assign articles = site.articles | where: "category", "git" %}

0 commit comments

Comments
 (0)