Respostas:
Lambdas individuais são traduzidas para diferentes classes pelo compilador. Por exemplo, a definição de lambda1 é equivalente a:
class SomeCompilerGeneratedTypeName {
public:
SomeCompilerGeneratedTypeName(...) { // Capture all the required variables here
}
void operator()(T& arg) const {
// ...
}
private:
// All the captured variables here ...
};
Portanto, dois tipos diferentes são gerados pelo compilador, o que causa uma incompatibilidade de tipo para auto lambda = condition ? lambda1 : lambda2;
O seguinte funcionaria:
auto lambda = condition ? std::function<void(T&)>(lambda1) : std::function<void(T&)>(lambda2);
Para destacar que as duas lambdas são realmente tipos diferentes, podemos usar <typeinfo>
a biblioteca padrão e o typeid
operador. Lambdas não são tipos polimórficos, portanto, o padrão garante que o operador 'typeid' seja avaliado em tempo de compilação. Isso mostra que o exemplo a seguir é válido mesmo se o RTTI estiver desativado:
#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;
}
A saída do programa é (com o GCC 8.3, veja no Gobolt ):
Z4mainEUlRZ4mainE1TE_/7654536205164302515
Z4mainEUlRZ4mainE1TE0_/10614161759544824066
SomeCompilerGeneratedTypeName1
eSomeCompilerGeneratedTypeName2
Curiosamente, se as lambdas são sem captura, o +
truque do operador pode ser empregado:
auto lambda1 = [](int arg) { ... };
auto lambda2 = [](int arg) { ... };
auto lambda = condition ? +lambda1 : +lambda2; // This compiles!
lambda(2019);
Isso funciona, porque +
converterá lambda em um ponteiro de função e os dois ponteiros de função terão o mesmo tipo (algo comovoid (*)(int)
).
Com o GCC e o Clang (mas não com o MSVC), +
pode ser omitido, as lambdas ainda serão convertidas em ponteiros de função.
O compilador não pode decidir que tipo auto
deve ser:
auto lambda = condition ? lambda1 : lambda2;
já que todo lambda tem um tipo diferente e único.
Uma maneira de funcionar é:
auto lambda = [&](T& arg) {
return (condition ? lambda1(arg) : lambda2(arg));
}
Ele não é compilado porque cada lambda tem um tipo único, não há um tipo comum para ?:
.
Você pode envolvê-los std::function<void(T&)>
, por exemplo
auto lamba1 = [&](T& arg) {
...
};
auto lambda2 = [&](T& arg) {
...
};
auto lambda = condition ? std::function(lambda1) : lambda2; // C++17 class template deduction