web-dev-qa-db-fra.com

Quel est le type de lambda lorsque déduit avec "auto" en C++ 11?

J'avais l'impression que le type d'un lambda est un pointeur de fonction. Quand j'ai effectué le test suivant, je l'ai trouvé faux ( demo ).

#define LAMBDA [] (int i) -> long { return 0; }
int main ()
{
  long (*pFptr)(int) = LAMBDA;  // ok
  auto pAuto = LAMBDA;  // ok
  assert(typeid(pFptr) == typeid(pAuto));  // assertion fails !
}

Le code ci-dessus manque-t-il un point? Sinon, quelle est la typeof une expression lambda déduite avec le mot clé auto?

110
iammilind

Le type d'une expression lambda n'est pas spécifié. 

Mais ils ne sont généralement que du sucre syntaxique pour les foncteurs. Un lambda est traduit directement en un foncteur. Tout ce qui se trouve à l'intérieur du [] est transformé en paramètres de constructeur et membres de l'objet foncteur, et les paramètres à l'intérieur de () sont transformés en paramètres de la fonction operator().

Un lambda qui ne capture aucune variable (rien à l'intérieur du []) ne peut être converti en un pointeur de fonction (MSVC2010 ne le supporte pas, si c'est votre compilateur, mais cette conversion fait partie de la norme).

Mais le type réel du lambda n'est pas un pointeur de fonction. C'est un type de foncteur non spécifié.

111
jalf

C'est une structure unique sans nom qui surcharge l'opérateur d'appel de fonction. Chaque instance d'un lambda introduit un nouveau type.

Dans le cas particulier d'un lambda sans capture, la structure comporte en outre une conversion implicite en un pointeur de fonction.

92
avakar

[C++11: 5.1.2/3]:Le type de l'expression lambda (qui est également le type de l'objet de fermeture) - est un type de classe non syndiqué unique et non nommé - appelé le type de fermeture - dont les propriétés sont décrites ci-dessous. Ce type de classe n'est pas un agrégat (8.5.1). Le type de fermeture est déclaré dans la plus petite portée de bloc, de classe ou d'espace de nom qui contient l'expression lambda correspondante . [..]

La clause énumère ensuite diverses propriétés de ce type. Voici quelques faits saillants:

[C++11: 5.1.2/5]: Le type de fermeture d'une expression lambda a un opérateur public inline (13.5.4) dont les paramètres et Les types de retour sont décrits par lambda-expression du paramètre-déclaration-clause et suivi-retour-type respectivement. [..]

[C++11: 5.1.2/6]: Le type de fermeture pour une expression lambda sans lambda-capture a une fonction de conversion const publique, non-virtuelle, non virtuelle, qui pointe vers une fonction ayant le même paramètre et renvoie le même type que l'opérateur de l'appel de fonction du type de fermeture. La valeur renvoyée par cette fonction de conversion doit être l’adresse d’une fonction qui, une fois appelée, a le même effet que l’appel de l’opérateur d’appel à la fonction du type de fermeture.

La conséquence de ce dernier passage est que, si vous utilisiez une conversion, vous seriez en mesure d’affecter LAMBDA à pFptr.

24
#include <iostream>
#include <typeinfo>

#define LAMBDA [] (int i)->long { return 0l; }
int main ()
{
  long (*pFptr)(int) = LAMBDA;  // ok
  auto pAuto = LAMBDA;  // ok

  std::cout<<typeid( *pAuto ).name() << std::endl;
  std::cout<<typeid( *pFptr ).name() << std::endl;

  std::cout<<typeid( pAuto ).name() << std::endl;
  std::cout<<typeid( pFptr ).name() << std::endl;
}

Les types de fonctions sont effectivement les mêmes, mais le lambda introduit un nouveau type (comme un foncteur).

1
BЈовић

Une solution pratique de Comment puis-je stocker un objet boost :: bind en tant que membre de la classe? , essayez boost::function<void(int)> ou std::function<void(int)>.

0
Gabriel

Notez également que lambda est convertible en pointeur de fonction. Cependant, typeid <> retourne un objet non-trvial qui devrait différer du pointeur de fonction lambda à générique. Donc, le test pour typeid <> n'est pas une hypothèse valide. En général, C++ 11 ne veut pas que nous nous préoccupions de la spécification de type, ce qui importe si un type donné est convertible en un type cible.

0
Syed Raihan