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

142

J'avais une perception que, le type d'un lambda est un pointeur de fonction. Lorsque j'ai effectué le test suivant, j'ai trouvé que c'était faux ( démo ).

#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, qu'est-ce que l' typeofexpression a lambda lorsqu'elle est déduite avec un automot-clé?

iammilind
la source
8
«Le type d'un lambda est un pointeur de fonction» - ce serait inefficace et manquerait tout l'intérêt des lambdas.
Konrad Rudolph

Réponses:

145

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

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

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

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

jalf
la source
1
MSVC2010 ne prend pas en charge la conversion en pointeur de fonction, contrairement à MSVC11. blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
KindDragon
18
+1 pour "pur sucre syntaxique pour les foncteurs". Une grande confusion potentielle peut être évitée en s'en souvenant.
Ben
4
un foncteur est quelque chose avec operator()essentiellement stackoverflow.com/questions/356950/c-functors-and-their-uses
TankorSmash
107

Il s'agit d'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 non capturant, la structure a en plus une conversion implicite en un pointeur de fonction.

Avakar
la source
2
Bonne réponse. Beaucoup plus précis que le mien. +1 :)
jalf
4
+1 pour la partie unicité, c'est très surprenant au premier abord et mérite l'attention.
Matthieu M.
Ce n'est pas vraiment important, mais le type est-il vraiment sans nom, ou ne lui a-t-on simplement pas donné de nom avant la compilation? IOW, pourrait-on utiliser RTTI pour trouver le nom choisi par le compilateur?
Ben
3
@Ben, il n'est pas nommé et en ce qui concerne le langage C ++, il n'y a pas de "nom sur lequel le compilateur décide". Le résultat de type_info::name()est défini par l'implémentation, il peut donc renvoyer n'importe quoi. En pratique, le compilateur nommera le type pour le bien de l'éditeur de liens.
avakar
1
Dernièrement, quand on me pose cette question, je dis généralement que le type de lambda a un nom, le compilateur le sait, il est tout simplement inexprimable.
Andre Kostur
24

[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-union unique et sans nom - appelé 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 étendue de bloc, étendue de classe ou étendue d'espace de noms qui contient l' expression lambda correspondante . [..]

La clause continue en listant les différentes propriétés de ce type. Voici quelques faits saillants:

[C++11: 5.1.2/5]:Le type de fermeture pour un lambda-expression a une population inlineopérateur d'appel de fonction (13.5.4) , dont les paramètres et le type de retour sont décrits par la lambda-expression de paramètre déclaration de clause et de type queue-retour respectivement. [..]

[C++11: 5.1.2/6]:Le type de fermeture pour un lambda-expression sans lambda-capture a un non explicite public non virtuelle fonction de conversion de const pointeur vers une fonction ayant les mêmes paramètres et types de retour comme opérateur d'appel de fonction du type de fermeture. La valeur renvoyée par cette fonction de conversion doit être l'adresse d'une fonction qui, lorsqu'elle est invoquée, a le même effet que l'appel de l'opérateur d'appel de fonction du type de fermeture.

La conséquence de ce dernier passage est que, si vous utilisiez une conversion, vous pourrez attribuer LAMBDAà pFptr.

Courses de légèreté en orbite
la source
3
#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 fonction sont en effet les mêmes, mais le lambda introduit un nouveau type (comme un foncteur).

BЈовић
la source
Je recommande l'approche démêlante CXXABI si vous suivez déjà cette voie. Au lieu de cela, j'utilise généralement __PRETTY_FUNCTION__, comme dans template<class T> const char* pretty(T && t) { return __PRETTY_FUNCTION__; }, et dépouille le supplément s'il commence à être encombré. Je préfère voir les étapes indiquées dans la substitution de modèle. Si vous manquez __PRETTY_FUNCTION__, il existe des alternatives pour MSVC, etc., mais les résultats dépendent toujours du compilateur pour la même raison que CXXABI est nécessaire.
John P
1

Il convient également de noter que lambda est convertible en pointeur de fonction. Cependant typeid <> renvoie un objet non-trvial qui devrait différer du pointeur lambda au pointeur de fonction générique. Le test pour typeid <> n'est donc 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, tout ce qui compte si un type donné est convertible en type cible.

Syed Raihan
la source
C'est juste, mais les types d'impression contribuent grandement à obtenir le bon type, sans parler de la capture des cas où le type est convertible mais ne satisfait pas d'autres contraintes. (Je pousserais toujours vers la "réification" des contraintes dans la mesure du possible, mais quelqu'un qui essaie de le faire a d'autant plus de raisons de montrer son travail pendant le développement.)
John P