Supposons que j'ai une generate_my_range
classe qui modélise un range
(en particulier, est regular
). Le code suivant est-il alors correct:
auto generate_my_range(int some_param) {
auto my_transform_op = [](const auto& x){ return do_sth(x); };
return my_custom_rng_gen(some_param) | ranges::views::transform(my_transform_op);
}
auto cells = generate_my_range(10) | ranges::to<std::vector>;
Est my_custom_rng_gen(some_param)
pris en valeur par le (premier) opérateur de canalisation, ou ai-je une référence pendant quand je quitte la generate_my_range
portée?
Serait-ce la même chose avec l'appel fonctionnel ranges::views::transform(my_custom_rng_gen(some_param),my_transform_op)
?
Serait-il correct si j'utilisais une référence lvalue? par exemple:
auto generate_my_range(int some_param) {
auto my_transform_op = [](const auto& x){ return do_sth(x); };
auto tmp_ref = my_custom_rng_gen(some_param);
return tmp_ref | ranges::views::transform(my_transform_op);
}
Si les plages sont prises par des valeurs pour ces opérations, que dois-je faire si je passe une référence lvalue à un conteneur? Dois-je utiliser un ranges::views::all(my_container)
motif?
Réponses:
Dans la bibliothèque de plages, il existe deux types d'opérations:
Les vues sont légères. Vous les transmettez par valeur et exigez que les conteneurs sous-jacents restent valides et inchangés.
De la documentation gammes-v3
et:
La destruction du conteneur sous-jacent invalide évidemment tous ses itérateurs.
Dans votre code, vous utilisez spécifiquement des vues - Vous utilisez
ranges::views::transform
. La pipe est simplement un sucre syntaxique qui facilite l'écriture telle qu'elle est. Vous devriez regarder la dernière chose dans le tuyau pour voir ce que vous produisez - dans votre cas, c'est une vue.S'il n'y avait pas d'opérateur de tuyau, cela ressemblerait probablement à ceci:
s'il y avait plusieurs transformations connectées de cette façon, vous pouvez voir à quel point cela deviendrait laid.
Ainsi, si
my_custom_rng_gen
produit une sorte de conteneur, que vous transformez puis renvoyez, ce conteneur est détruit et vous avez des références pendantes de votre vue. Simy_custom_rng_gen
c'est une autre vue d'un conteneur qui vit en dehors de ces étendues, tout va bien.Cependant, le compilateur doit être capable de reconnaître que vous appliquez une vue sur un conteneur temporaire et de vous rencontrer avec une erreur de compilation.
Si vous souhaitez que votre fonction renvoie une plage en tant que conteneur, vous devez explicitement «matérialiser» le résultat. Pour cela, utilisez l'
ranges::to
opérateur dans la fonction.Mise à jour: Pour être plus explicite concernant votre commentaire "où la documentation indique-t-elle que la composition de la plage / des tuyaux prend et stocke une vue?"
La pipe est simplement un sucre syntaxique pour relier les choses dans une expression facile à lire. Selon la façon dont il est utilisé, il peut ou non renvoyer une vue. Cela dépend de l'argument de droite. Dans votre cas, c'est:
Ainsi, l'expression renvoie tout ce qui
views::transform
retourne.Maintenant, en lisant la documentation de la transformation:
Il renvoie donc une plage, mais comme il s'agit d'un opérateur paresseux, cette plage qu'il renvoie est une vue, avec toute sa sémantique.
la source
ranges::views::all(my_container)
? Et si une vue est transmise au tuyau? Est-ce qu'il reconnaît qu'il a passé un conteneur ou une vue? En a-t-il besoin? Comment?my_custom_rng_gen
. Comment exactement le tuyau ettransform
interagir sous le capot n'est pas important. L'expression entière prend une plage comme argument (un conteneur ou une vue vers un conteneur) et renvoie une vue différente à ce conteneur. La valeur de retour ne sera jamais propriétaire du conteneur, car il s'agit d'une vue.Tiré de la documentation des gammes-v3 :
et
Puisque vous avez dit que la plage temporaire peut être considérée comme un conteneur, votre fonction renvoie une référence pendante.
En d'autres termes, vous devez vous assurer que la plage sous-jacente survit à la vue, sinon vous êtes en difficulté.
la source