Skip to content

Commit 92309e2

Browse files
committed
C++ Auto : Trailing return type
1 parent 4957e51 commit 92309e2

File tree

1 file changed

+91
-1
lines changed

1 file changed

+91
-1
lines changed

_articles/c++_auto.md

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ auto smartPointer = std::make_unique<MyClass>();
195195
auto lambda = [](auto lhs, auto rhs) { return lhs + rhs; };
196196
{% endhighlight %}
197197

198+
> Le **left-to-right** rend la lecture beaucoup plus naturelle:<br>
199+
> Lorsqu'on décrit une variable, on dit "Une variable ``duration`` qui vaut 10 secondes.".<br>
200+
> On ne dit pas "En secondes, une variable ``duration`` qui vaut 10."
201+
198202
### ``auto`` force l'initialisation
199203

200204
Sans ``auto``, il est possible de **déclarer des variables sans les initialiser**.
@@ -635,6 +639,21 @@ auto sum(int lhs, int rhs) -> int
635639
}
636640
{% endhighlight %}
637641

642+
> Le **left-to-right** rend la lecture beaucoup plus naturelle:<br>
643+
> Lorsqu'on décrit une fonction, on dit "Une fonction ``sum`` qui prend deux int et qui retourne un int".<br>
644+
> On ne dit pas "Un int retourné par une fonction ``sum`` qui prend deux int."
645+
646+
> Certains pourraient trouver cette syntaxe absurde, car elle semble préciser **deux types au lieu d'un** (``auto`` et ``int``).
647+
> Mais ici, **``auto`` n'est pas un type**.<br>
648+
> ``auto main() -> int`` semble ridicule de premier abord, mais c'est uniquement parce que **nous n'y sommes pas habitués**.
649+
> Il n'y a rien d'intrinsèquement ridicule dans cette syntaxe.<br>
650+
> Le seul défaut qu'on pourrait y voir est que ça ne se distingue pas vraiment des autres utilisations de ``auto``. Un mot clef ``func``, ``function`` (Typescript) ou [``fn`` (Rust)](https://doc.rust-lang.org/book/ch03-03-how-functions-work.html) serait plus approprié.
651+
{: .block-warning }
652+
653+
Cette nouvelle écriture offre plusieurs autres avantages:
654+
655+
### Type de retour dépendant des paramètres
656+
638657
Cette écriture permet entre autre de définir un type de retour qui dépend des paramètres de la fonction, puisque ceux-ci sont connus avant.
639658

640659
{% highlight cpp %}
@@ -663,6 +682,77 @@ decltype(lhs + rhs) sum(Lhs lhs, Rhs rhs)
663682
664683
Le compilateur comprend les déclarations dans l'ordre dans lequel il les lit. Et comme il lit les fichiers de haut en bas et de gauche à droite, il ne connait pas encore ``lhs`` et ``rhs`` à l'instant où on les utilise dans ``decltype(lhs + rhs)``.
665684

685+
### Fonctions retournant un pointeur de fonction
686+
687+
Lorsqu'on souhaite créer une fonction qui retourne un pointeur de fonction, parvenir à écrire sa déclaration est un défi en soi, et la comprendre l'est encore plus.
688+
689+
{% highlight cpp %}
690+
int (*getFunction())(int); // Retourne un pointeur de fonction prenant un int et retournant un int
691+
{% endhighlight %}
692+
693+
Il devient facile de l'écrire et de la comprendre en utilisant la syntaxe du *trailing return type*:
694+
{% highlight cpp %}
695+
auto getFunction() -> int (*)(int);
696+
{% endhighlight %}
697+
698+
### Simplifie la résolution des scopes
699+
700+
Un autre avantage non négligeable du *trailing return type* est une simplification de la résolution des scopes:
701+
{% highlight cpp %}
702+
struct Foo
703+
{
704+
using Int = std::int16_t;
705+
Int getNumber();
706+
};
707+
708+
using Int = std::int64_t;
709+
710+
Int Foo::getNumber() { return 0; } // Ne compile pas, type de retour incorrect
711+
Foo::Int Foo::getNumber() { return 0; } // Ok
712+
713+
auto Foo::getNumber() -> Int { return 0; } // Ok
714+
{% endhighlight %}
715+
716+
Le *trailing return type* étant après le nom de la fonction, il prend en compte **le même scope** que celle-ci.<br>
717+
Lorsqu'on utilise beaucoup de namespaces, nested classes et alias de types, le *trailing return type* permet d'avoir une syntaxe beaucoup plus simple et claire.
718+
719+
### Levée d'ambiguïté par le trailing return type
720+
721+
Lorsqu'on définie une fonction depuis le namespace global, le *trailing return type* permet même de lever une ambiguïté du compilateur:
722+
{% highlight cpp %}
723+
using Int = std::int64_t;
724+
725+
Int ::Foo::getNumber() { return 0; }
726+
{% endhighlight %}
727+
728+
Clang:
729+
{% highlight console %}
730+
<source>:12:1: error: 'Int' (aka 'int') is not a class, namespace, or enumeration
731+
12 | Int ::Foo::getNumber() { return 0; }
732+
| ^
733+
1 error generated.
734+
{% endhighlight %}
735+
736+
MSVC:
737+
{% highlight console %}
738+
<source>(12): error C2825: 'Int': must be a class or namespace when followed by '::'
739+
<source>(12): error C2510: 'Int': left of '::' must be a class/struct/union
740+
<source>(12): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
741+
Compiler returned: 2
742+
{% endhighlight %}
743+
744+
Ici, ``Int ::Foo::getNumber`` peut être interprété par le compilateur comme étant ``Int::Foo::getNumber``.<br>
745+
Il s'attend donc à ce que ``Int`` soit une struct, une classe, un namespace, une enum ou une union.
746+
747+
Pas d'ambiguïté possible avec le *trailing return type*.
748+
{% highlight cpp %}
749+
using Int = std::int64_t;
750+
751+
auto ::Foo::getNumber() -> Int { return 0; }
752+
{% endhighlight %}
753+
754+
### Syntaxe uniforme avec les lambdas
755+
666756
Cette nouvelle syntaxe apporte aussi une **uniformisation entre la syntaxe des fonctions et celle des lambdas**.
667757

668758
Les lambdas (C++11) s'écrivent de la façon suivante, avec le type de retour à droite:
@@ -675,7 +765,7 @@ auto sum = [](int lhs, int rhs) -> int {
675765
A noter qu'ici, ``auto`` n'est pas le type de la valeur de retour de la lambda, mais le type de la lambda elle-même.<br>
676766
Ca a été abordée dans la [section précédente](#placeholder-type-specifiers-depuis-c11).
677767

678-
> En résumé, utiliser ``auto`` avec le trailing return type permet d'**uniformiser** la manière dont les types de retour sont déclarés et assure une meilleure lisibilité, surtout dans les fonctions dont le type de retour dépend des paramètres.<br>
768+
> En résumé, utiliser ``auto`` avec le *trailing return type* permet de **simplifier et clarifier** les types retournés par les fonctions, **lever des ambiguïtés** du compilateur, **uniformiser** la manière dont les types de retour sont déclarés et permet aux fonctions de **retourner des types dépendant des paramètres**.<br>
679769
> Cette pratique est **recommandée en C++ moderne**.
680770
681771
## AAA (Almost Always Auto) (depuis C++11)

0 commit comments

Comments
 (0)