Risposte:
I singoli lambda vengono tradotti in diverse classi dal compilatore. Ad esempio, la definizione di lambda1 è equivalente a:
class SomeCompilerGeneratedTypeName {
public:
SomeCompilerGeneratedTypeName(...) { // Capture all the required variables here
}
void operator()(T& arg) const {
// ...
}
private:
// All the captured variables here ...
};
Pertanto, il compilatore genera due tipi diversi, che causa un'incompatibilità del tipo per auto lambda = condition ? lambda1 : lambda2;
Quanto segue funzionerebbe:
auto lambda = condition ? std::function<void(T&)>(lambda1) : std::function<void(T&)>(lambda2);
Per evidenziare che entrambi i lambda sono effettivamente tipi diversi, possiamo usare <typeinfo>
dalla libreria standard e typeid
dall'operatore. I lambda non sono tipi polimorfici, quindi lo standard garantisce che l'operatore 'typeid' sia valutato al momento della compilazione. Ciò dimostra che il seguente esempio è valido anche se RTTI è disabilitato:
#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;
}
L'output del programma è (con GCC 8.3, vedi su Gobolt ):
Z4mainEUlRZ4mainE1TE_/7654536205164302515
Z4mainEUlRZ4mainE1TE0_/10614161759544824066
SomeCompilerGeneratedTypeName1
eSomeCompilerGeneratedTypeName2
Curiosamente, se le lambda sono prive di cattura, si +
può impiegare il trucco dell'operatore :
auto lambda1 = [](int arg) { ... };
auto lambda2 = [](int arg) { ... };
auto lambda = condition ? +lambda1 : +lambda2; // This compiles!
lambda(2019);
Funziona, perché +
convertirà lambda in un puntatore a funzione ed entrambi i puntatori a funzione hanno lo stesso tipo (qualcosa del genere void (*)(int)
).
Con GCC e Clang (ma non con MSVC), +
possono essere omessi, i lambda verranno comunque convertiti in puntatori a funzione.
Il compilatore non può decidere quale tipo auto
dovrebbe essere:
auto lambda = condition ? lambda1 : lambda2;
poiché ogni lambda ha un tipo diverso e unico.
Un modo che funzionerà è:
auto lambda = [&](T& arg) {
return (condition ? lambda1(arg) : lambda2(arg));
}
Non viene compilato perché ogni lambda ha un tipo univoco, non esiste un tipo comune per ?:
.
Potresti avvolgerli std::function<void(T&)>
, ad es
auto lamba1 = [&](T& arg) {
...
};
auto lambda2 = [&](T& arg) {
...
};
auto lambda = condition ? std::function(lambda1) : lambda2; // C++17 class template deduction