Respuestas:
El compilador traduce las lambdas individuales a diferentes clases. Por ejemplo, la definición de lambda1 es equivalente a:
class SomeCompilerGeneratedTypeName {
public:
SomeCompilerGeneratedTypeName(...) { // Capture all the required variables here
}
void operator()(T& arg) const {
// ...
}
private:
// All the captured variables here ...
};
Por lo tanto, el compilador genera dos tipos diferentes, lo que provoca una incompatibilidad de tipos para auto lambda = condition ? lambda1 : lambda2;
Lo siguiente funcionaría:
auto lambda = condition ? std::function<void(T&)>(lambda1) : std::function<void(T&)>(lambda2);
Para resaltar que ambas lambdas son de hecho tipos diferentes, podemos usar <typeinfo>
desde la biblioteca estándar y el typeid
operador. Las lambdas no son tipos polimórficos, por lo que el estándar garantiza que el operador 'typeid' sea evaluado en tiempo de compilación. Esto muestra que el siguiente ejemplo es válido incluso si RTTI está desactivado:
#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 salida del programa es (con GCC 8.3, ver en Gobolt ):
Z4mainEUlRZ4mainE1TE_/7654536205164302515
Z4mainEUlRZ4mainE1TE0_/10614161759544824066
SomeCompilerGeneratedTypeName1
ySomeCompilerGeneratedTypeName2
Curiosamente, si las lambdas no requieren captura, +
se puede emplear el truco del operador :
auto lambda1 = [](int arg) { ... };
auto lambda2 = [](int arg) { ... };
auto lambda = condition ? +lambda1 : +lambda2; // This compiles!
lambda(2019);
Esto funciona, porque +
convertirá lambda en un puntero de función, y ambos punteros de función tienen el mismo tipo (algo así como void (*)(int)
).
Con GCC y Clang (pero no con MSVC), +
pueden omitirse, las lambdas aún se convertirán en punteros de función.
El compilador no puede decidir qué tipo auto
debería ser:
auto lambda = condition ? lambda1 : lambda2;
ya que cada lambda tiene un tipo diferente y único.
Una forma en que funcionará es:
auto lambda = [&](T& arg) {
return (condition ? lambda1(arg) : lambda2(arg));
}
No se compila porque cada lambda tiene un tipo único, no hay un tipo común para ?:
.
Podrías envolverlos std::function<void(T&)>
, por ej.
auto lamba1 = [&](T& arg) {
...
};
auto lambda2 = [&](T& arg) {
...
};
auto lambda = condition ? std::function(lambda1) : lambda2; // C++17 class template deduction