c++ c++11 lambda conditional-operator

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