Réponses:
Les lambdas individuels sont traduits en différentes classes par le compilateur. Par exemple, la définition de lambda1 équivaut à:
class SomeCompilerGeneratedTypeName {
public:
SomeCompilerGeneratedTypeName(...) { // Capture all the required variables here
}
void operator()(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' typeid
opé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é:
#include <iostream>
#include <typeinfo>
int main()
{
struct T {
};
auto lambda1 = [&](T& arg) {
return;
};
auto lambda2 = [&](T& arg) {
return;
};
std::cout << typeid(lambda1).name() << "/" << typeid(lambda1).hash_code() << std::endl;
std::cout << typeid(lambda2).name() << "/" << typeid(lambda2).hash_code() << std::endl;
return 0;
}
La sortie du programme est (avec GCC 8.3, voir sur Gobolt ):
Z4mainEUlRZ4mainE1TE_/7654536205164302515
Z4mainEUlRZ4mainE1TE0_/10614161759544824066
SomeCompilerGeneratedTypeName1
etSomeCompilerGeneratedTypeName2
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.
Le compilateur ne peut pas décider quel type auto
doit être:
auto lambda = condition ? lambda1 : lambda2;
puisque chaque lambda a un type différent et unique.
Une façon qui fonctionnera est:
auto lambda = [&](T& arg) {
return (condition ? lambda1(arg) : lambda2(arg));
}
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 exemple
auto lamba1 = [&](T& arg) {
...
};
auto lambda2 = [&](T& arg) {
...
};
auto lambda = condition ? std::function(lambda1) : lambda2; // C++17 class template deduction