Les lambdas individuels sont traduits en différentes classes par le compilateur. Par exemple, la définition de lambda1 équivaut à:
classSomeCompilerGeneratedTypeName{public:SomeCompilerGeneratedTypeName(...){// Capture all the required variables here}voidoperator()(T& arg)const{// ...}private:// All the captured variables here ...};
Par conséquent, deux types différents sont générés par le compilateur, ce qui provoque une incompatibilité de type pour auto lambda = condition ? lambda1 : lambda2;
Les éléments suivants fonctionneraient:
auto lambda = condition ? std::function<void(T&)>(lambda1): std::function<void(T&)>(lambda2);
Pour souligner que les deux lambdas sont en effet de types différents, nous pouvons utiliser à <typeinfo>partir de la bibliothèque standard et de l' typeidopérateur. Les lambdas ne sont pas des types polymorphes, donc la norme garantit que l'opérateur «typeid» est évalué au moment de la compilation. Cela montre que l'exemple suivant est valide même si RTTI est désactivé:
L'erreur complète est "error: operands to?: Have different types 'f (const std :: vector <int> &, size_t, size_t) [with T = unsigned char; size_t = long unsigned int] :: <lambda (unsigned char & )> 'et' f (const std :: vector <int> &, size_t, size_t) [avec T = unsigned char; size_t = long unsigned int] :: <lambda (unsigned char &)> '", dans lequel je vois identiques tous types et formats.
vache
1
@cow Parce que les lambda ont en eux-mêmes la même signature, le compilateur, afin de masquer ses détails d'implémentation et de donner une erreur plus compréhensible, vous donne l'emplacement et la signature des deux lambdas qui sont identiques. Mais à la fin, ils sont toujours interprétés comme SomeCompilerGeneratedTypeName1etSomeCompilerGeneratedTypeName2
Xatyrian
1
@cow j'ai ajouté un exemple qui met en évidence le début de la réponse, vous pourriez le trouver intéressant
Xatyrian
12
Curieusement, si les lambdas sont sans capture, une +astuce d' opérateur peut être utilisée:
auto lambda1 =[](int arg){...};auto lambda2 =[](int arg){...};auto lambda = condition ?+lambda1 :+lambda2;// This compiles!
lambda(2019);
Cela fonctionne, car +convertira lambda en un pointeur de fonction, et les deux pointeurs de fonction ont le même type (quelque chose comme void (*)(int)).
Avec GCC et Clang (mais pas avec MSVC), +peuvent être omis, les lambdas seront toujours convertis en pointeurs de fonction.
Puisque 2 lambdas ( lambda1et lambda2) sont de 2 types différents, ?:ne peut pas déduire le type de retour pour lambdafrom lambda1et lambda2. Cela se produit car ces 2 ne sont pas convertibles entre eux.
SomeCompilerGeneratedTypeName1
etSomeCompilerGeneratedTypeName2
Curieusement, si les lambdas sont sans capture, une
+
astuce d' opérateur peut être utilisée:Cela fonctionne, car
+
convertira lambda en un pointeur de fonction, et les deux pointeurs de fonction ont le même type (quelque chose commevoid (*)(int)
).Avec GCC et Clang (mais pas avec MSVC),
+
peuvent être omis, les lambdas seront toujours convertis en pointeurs de fonction.la source
Le compilateur ne peut pas décider quel type
auto
doit être:puisque chaque lambda a un type différent et unique.
Une façon qui fonctionnera est:
la source
Il ne compile pas car chaque lambda a un type unique, il n'y a pas de type commun pour
?:
.Vous pouvez les envelopper
std::function<void(T&)>
, par exemplela source
Puisque 2 lambdas (
lambda1
etlambda2
) sont de 2 types différents,?:
ne peut pas déduire le type de retour pourlambda
fromlambda1
etlambda2
. Cela se produit car ces 2 ne sont pas convertibles entre eux.la source