J'ai trouvé que lvalue
les fermetures lambda peuvent toujours être passées en tant que paramètres de fonction rvalue
.
Voir la démonstration simple suivante.
#include <iostream>
#include <functional>
using namespace std;
void foo(std::function<void()>&& t)
{
}
int main()
{
// Case 1: passing a `lvalue` closure
auto fn1 = []{};
foo(fn1); // works
// Case 2: passing a `lvalue` function object
std::function<void()> fn2 = []{};
foo(fn2); // compile error
return 0;
}
Le cas 2 est le comportement standard (je viens d'utiliser un std::function
à des fins de démonstration, mais tout autre type se comporterait de la même manière).
Comment et pourquoi fonctionne le cas 1? Quel est l'état de fn1
fermeture après le retour de la fonction?
Comment et pourquoi fonctionne le cas 1?
L'appel de foo
nécessite une instance de std::function<void()>
qui se lie à une référence de valeur . std::function<void()>
peut être construit à partir de tout objet appelable compatible avec la signature void()
.
Tout d'abord, un objet std::function<void()>
temporaire est construit à partir de []{}
. Le constructeur utilisé est # 5 ici , qui copie la fermeture dans l'instance std::function
:
template< class F > function( F f );
Initialise la cible avec
std::move(f)
. Sif
est un pointeur nul pour la fonction ou un pointeur nul pour le membre,*this
Sera vide après l'appel.
Ensuite, l'instance temporaire function
est liée à la référence rvalue.
Quel est l'état de fermeture de fn1 après le retour de la fonction?
Comme précédemment, car il a été copié dans une instance std::function
. La fermeture d'origine n'est pas affectée.
Un lambda n'est pas un std::function
. La référence ne lie pas directement.
Le cas 1 fonctionne car les lambdas sont convertibles en std::function
s. Cela signifie qu'un _ std::function
est matérialisé par copiefn1
. Ledit temporaire peut être lié à une référence rvalue, et donc l'argument correspond au paramètre.
Et la copie est aussi la raison pour laquelle fn1
n'est absolument pas affecté par tout ce qui se passe dans foo
.
Quel est l'état de fermeture de fn1 après le retour de la fonction?
fn1
est apatride, car il ne capture rien.
Comment et pourquoi fonctionne le cas 1?
Cela fonctionne parce que l'argument est de type différent de celui référencé par rvalue. En raison d'un type différent, les conversions implicites sont prises en compte. Puisque le lambda est Callable pour les arguments de ce std::function
, il y est implicitement convertible via le constructeur de conversion de modèle de std::function
. Le résultat de la conversion est une valeur pr et peut donc être lié à la référence rvalue.