C++ asignación ternaria de lambda
c++11 conditional-operator (5)
¿Alguna idea de por qué el siguiente fragmento no se compila? Se queja con un error "error: operandos a?: Tener diferentes tipos"
auto lambda1 = [&](T& arg) {
...
};
auto lambda2 = [&](T& arg) {
...
};
auto lambda = condition ? lambda1 : lambda2;
Curiosamente, si las lambdas no requieren captura, se puede emplear operador
+
truco:
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), se puede omitir
+
, las lambdas aún se convertirán en punteros de función.
Dado que 2 lambdas (
lambda1
y
lambda2
) son 2 tipos diferentes,
?:
No puede deducir el tipo de retorno para
lambda
de
lambda1
y
lambda2
.
Esto sucede porque estos 2 no son convertibles entre sí.
El compilador no puede decidir qué tipo de
auto
debería ser:
auto lambda = condition ? lambda1 : lambda2;
ya que cada lambda tiene un tipo diferente y único.
Una forma que funcionará es:
auto lambda = [&](T& arg) {
return (condition ? lambda1(arg) : lambda2(arg));
}
El compilador traduce lambda individual a diferentes clases.
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, por lo tanto, ¿una incompatibilidad de tipos para
auto lambda = condition ? lambda1 : lambda2;
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>
de la biblioteca estándar y el operador
typeid
.
Debido a que las lambdas no son tipos polimórficos, el estándar garantiza que el operador
typeid
se evalúa en tiempo de compilación, por lo 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
No se compila porque cada lambda tiene un tipo único, no hay un tipo común para
?:
.
Puede envolverlos en
std::function<void(T&)>
, por ejemplo
auto lamba1 = [&](T& arg) {
...
};
auto lambda2 = [&](T& arg) {
...
};
auto lambda = condition ? std::function(lambda1) : lambda2; // C++17 class template deduction