web-dev-qa-db-fra.com

Obtention du pointeur de fonction sur lambda?

Je veux pouvoir obtenir un pointeur de fonction vers un lambda en C++.

Je peux faire:

int (*c)(int) = [](int i) { return i; };

Et, bien sûr, les travaux suivants - même s'ils ne créent pas de pointeur de fonction.

auto a = [](int i) { return i; };

Mais ce qui suit:

auto *b = [](int i) { return i; };

Donne cette erreur dans GCC:

main.cpp: In function 'int main()':
main.cpp:13:37: error: unable to deduce 'auto*' from '<lambda closure object>main()::<lambda(int)>{}'
     auto *b = [](int i) { return i; };
                                      ^
main.cpp:13:37: note:   mismatched types 'auto*' and 'main()::<lambda(int)>'

Il semble arbitraire qu'un lambda puisse être converti en un pointeur de fonction sans problème, mais le compilateur ne peut pas inférer le type de fonction et créer un pointeur vers celui-ci en utilisant auto *. Surtout quand il peut implicitement convertir un unique, lambda type vers un pointeur de fonction:

int (*g)(int) = a;

J'ai créé un petit banc d'essai à http://coliru.stacked-crooked.com/a/2cbd62c8179dc61b qui contient les exemples ci-dessus. Ce comportement est le même sous C++ 11 et C++ 14.

35
oconnor0

Cela échoue:

auto *b = [](int i) { return i; };

parce que le lambda n'est pas un pointeur. auto ne permet pas les conversions. Même si le lambda est convertible en quelque chose qui est un pointeur, cela ne va pas être fait pour vous - vous devez le faire vous-même. Que ce soit avec un casting:

auto *c = static_cast<int(*)(int)>([](int i){return i;});

Ou avec n peu de sorcellerie :

auto *d = +[](int i) { return i; };
44
Barry

Surtout quand il peut implicitement convertir un type lambda unique en un pointeur de fonction:

Mais il ne peut pas le convertir en "un pointeur de fonction". Il ne peut le convertir qu'en un pointeur en une signature de fonction spécifique. Cela échouera:

int (*h)(float) = a;

Pourquoi cela échoue-t-il? Parce qu'il n'y a pas de conversion implicite valide de a vers h ici.

La conversion pour lambdas n'est pas la magie du compilateur. La norme dit simplement que le type de fermeture lambda, pour les lambdas non capturants et non génériques, a un opérateur de conversion implicite pour les pointeurs de fonction correspondant à la signature de sa surcharge operator(). Les règles d'initialisation de int (*g)(int) de a permettent d'utiliser des conversions implicites, et donc le compilateur invoquera cet opérateur.

auto ne permet pas d'utiliser des opérateurs de conversion implicites; il prend le type tel quel (suppression des références, bien sûr). auto* Ne fait pas non plus de conversions implicites. Alors pourquoi invoquerait-il une conversion implicite pour une fermeture lambda et non pour un type défini par l'utilisateur?

7
Nicol Bolas

Le code lambda ne fonctionne pas pour la même raison que cela ne fonctionne pas:

struct foo {
  operator int*() const {
    static int x;
    return &x;
  }
};

int* pint = foo{};
auto* pint2 = foo{}; // does not compile

ou même:

template<class T>
void test(T*) {};
test(foo{});

Le lambda a un opérateur qui le convertit implicitement en un pointeur de fonction (particulier), tout comme foo.

auto ne fait pas de conversion. Déjà. Se comporte automatiquement comme un paramètre class T Pour une fonction de modèle où son type est déduit.

Comme le type sur le côté droit n'est pas un pointeur, il ne peut pas être utilisé pour initialiser une variable auto*.

Les lambdas ne sont pas des pointeurs de fonction. Les lambdas ne sont pas des std::function S. Ce sont des objets fonction auto-écrits (objets avec une operator()).

Examinez ceci:

void (*ptr)(int) = [](auto x){std::cout << x;};
ptr(7);

il compile et fonctionne en gcc (je ne sais pas si c'est une extension, maintenant que j'y pense). Cependant, que ferait auto* ptr = [](auto x){std::cout << x;}?

Cependant, unary + Est un opérateur qui fonctionne sur les pointeurs (et ne leur fait presque rien), mais pas dans foo ou un lambda.

Donc

auto* pauto=+foo{};

Et

auto* pfun=+[](int x){};

Les deux fonctionnent comme par magie.