Lambdas individuais são traduzidas para diferentes classes pelo compilador. Por exemplo, a definição de lambda1 é equivalente a:
classSomeCompilerGeneratedTypeName{public:SomeCompilerGeneratedTypeName(...){// Capture all the required variables here}voidoperator()(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 typeidoperador. 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:
O erro completo é "error: operandos para?: Têm tipos diferentes 'f (const std :: vector <int> &, size_t, size_t) [com T = caracter não assinado; size_t = int longo sem sinal] :: <lambda (caracter não assinado & )> 'e' f (const std :: vetor <int> &, size_t, size_t) [com T = char não assinado; size_t = long sem assinatura int] :: <lambda (char não assinado &)> '", na qual vejo todos os tipos e formatos idênticos.
vaca
11
@ cow Como o próprio lambda possui a mesma assinatura, o compilador, para ocultar seus detalhes de implementação e fornecer um erro mais compreensível, fornece a localização e a assinatura de ambos os lambdas que são idênticos. Mas no final, eles ainda são interpretados como SomeCompilerGeneratedTypeName1eSomeCompilerGeneratedTypeName2
Xatyrian
11
@cow eu adicionei um exemplo que destaca o início da resposta, você pode achar que é interessante
Xatyrian
12
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.
Como 2 lambdas ( lambda1e lambda2) são 2 tipos diferentes, ?:não é possível deduzir o tipo de retorno para lambdafrom lambda1e lambda2. Isso acontece porque esses 2 não são conversíveis entre si.
SomeCompilerGeneratedTypeName1
eSomeCompilerGeneratedTypeName2
Curiosamente, se as lambdas são sem captura, o
+
truque do operador pode ser empregado: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.fonte
O compilador não pode decidir que tipo
auto
deve ser:já que todo lambda tem um tipo diferente e único.
Uma maneira de funcionar é:
fonte
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 exemplofonte
Como 2 lambdas (
lambda1
elambda2
) são 2 tipos diferentes,?:
não é possível deduzir o tipo de retorno paralambda
fromlambda1
elambda2
. Isso acontece porque esses 2 não são conversíveis entre si.fonte