36 votes

Obtention d'un pointeur de fonction vers un lambda ?

Je veux être capable d'obtenir un pointeur de fonction vers un lambda en C++.

Je peux le faire :

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

Et, bien sûr, ce qui suit fonctionne - même s'il ne crée pas un 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 pointeur de fonction sans problème, mais que le compilateur ne puisse pas déduire le type de la fonction et créer un pointeur vers celle-ci en utilisant la fonction auto * . Surtout lorsqu'il peut convertir implicitement un unique, lambda type à 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.

46voto

Barry Points 45207

C'est raté :

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

car le lambda n'est pas un pointeur. auto ne permet pas les conversions. Même si la 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 de la sorcellerie :

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

8voto

Nicol Bolas Points 133791

Surtout lorsqu'il peut implicitement convertir un type unique, lambda, en un pointeur de fonction :

Mais il ne peut pas le convertir en "un pointeur de fonction". Il peut seulement le convertir en un pointeur vers une fonction spécifique signature de la fonction. Ce sera un échec :

int (*h)(float) = a;

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

La conversion pour les lambdas n'est pas une magie du compilateur. La norme stipule simplement que le type de fermeture lambda, pour les lambdas non capturants et non génériques, possède un opérateur de conversion implicite pour les pointeurs de fonction correspondant à la signature de sa fonction operator() surcharge. Les règles d'initialisation 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 (en supprimant les 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 pas pour un type défini par l'utilisateur ?

5voto

Yakk Points 31636

Le code lambda ne fonctionne pas pour la même raison que ceci 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 possède un opérateur qui le convertit implicitement en un pointeur de fonction (particulier), tout comme foo .

auto ne fait pas de conversion. Jamais. Auto se comporte comme un class T à une fonction modèle où son type est déduit.

Comme le type sur la droite n'est pas un pointeur, il ne peut pas être utilisé pour initialiser un fichier auto* variable.

Les lambdas ne sont pas des pointeurs de fonction. Les lambdas ne sont pas std::function s. Il s'agit d'objets de fonction auto-écrits (objets avec un nom de fonction de type operator() ).

Examinez ceci :

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

il compile et fonctionne dans gcc (je ne suis pas certain qu'il s'agisse d'une extension, maintenant que j'y pense). Cependant, ce qui auto* ptr = [](auto x){std::cout << x;} censé faire ?

Cependant, l'unaire + est un opérateur qui fonctionne sur les pointeurs (et qui ne leur fait presque rien), mais pas en foo ou un lambda.

Alors

auto* pauto=+foo{};

Et

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

Les deux fonctionnent, comme par magie.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X