web-dev-qa-db-fra.com

Les valeurs de fermeture lambda peuvent être passées en tant que paramètres de référence rvalue

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?

18
Sumudu

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). Si f 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.

8
Vittorio Romeo

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::functions. 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.

5
eerorika