You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Est-ce un nombre de type ``int``, initialisé en lui fournissant ``number`` casté en ``int`` (avec ``int(number)``) ?
279
276
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>
281
278
282
279
> Le langage C **autorise les parenthèses superflues autour des paramètres** des fonctions.
283
280
{: .block-warning }
284
281
285
282
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**).
286
283
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**.
288
285
289
286
> Si les warnings (``-Wvexing-parse``) sont activés sur votre compilateur, celui-ci devrait être assez explicite quant à la raison de cette ambigüité.
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).
1460
1469
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.
1462
1471
1463
1472
{% highlight cpp %}
1464
1473
function(auto(expr));
1465
1474
function(auto{expr});
1466
1475
{% endhighlight %}
1467
1476
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:
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.
0 commit comments