El compilador traduce las lambdas individuales a diferentes clases. Por ejemplo, la definición de lambda1 es equivalente a:
classSomeCompilerGeneratedTypeName{public:SomeCompilerGeneratedTypeName(...){// Capture all the required variables here}voidoperator()(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 typeidoperador. 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:
El error completo es "error: operandos a?: Tiene diferentes tipos 'f (const std :: vector <int> &, size_t, size_t) [con T = unsigned char; size_t = long unsigned int] :: <lambda (unsigned char & )> 'y' f (const std :: vector <int> &, size_t, size_t) [with T = unsigned char; size_t = long unsigned int] :: <lambda (unsigned char &)> '", en el que veo idénticos todos los tipos y formatos.
vaca
1
@cow Debido a que el lambda en sí mismo tiene la misma firma, el compilador, para ocultar sus detalles de implementación y dar un error más comprensible, le da la ubicación y la firma de ambos lambdas que son idénticos. Pero al final, todavía se interpretan como SomeCompilerGeneratedTypeName1ySomeCompilerGeneratedTypeName2
Xatyrian
1
@cow agregué un ejemplo que resalta el comienzo de la respuesta, puede que le resulte interesante
Xatyrian
12
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.
Dado que 2 lambdas ( lambda1y lambda2) son 2 tipos diferentes, ?:no se puede deducir el tipo de retorno de lambdafrom lambda1y lambda2. Esto sucede porque estos 2 no son convertibles entre sí.
SomeCompilerGeneratedTypeName1
ySomeCompilerGeneratedTypeName2
Curiosamente, si las lambdas no requieren captura,
+
se puede emplear el truco del operador :Esto funciona, porque
+
convertirá lambda en un puntero de función, y ambos punteros de función tienen el mismo tipo (algo así comovoid (*)(int)
).Con GCC y Clang (pero no con MSVC),
+
pueden omitirse, las lambdas aún se convertirán en punteros de función.fuente
El compilador no puede decidir qué tipo
auto
debería ser:ya que cada lambda tiene un tipo diferente y único.
Una forma en que funcionará es:
fuente
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.fuente
Dado que 2 lambdas (
lambda1
ylambda2
) son 2 tipos diferentes,?:
no se puede deducir el tipo de retorno delambda
fromlambda1
ylambda2
. Esto sucede porque estos 2 no son convertibles entre sí.fuente