Poszczególne lambdy są tłumaczone przez kompilator na różne klasy. Na przykład definicja lambda1 jest równoważna z:
classSomeCompilerGeneratedTypeName{public:SomeCompilerGeneratedTypeName(...){// Capture all the required variables here}voidoperator()(T& arg)const{// ...}private:// All the captured variables here ...};
Dlatego kompilator generuje dwa różne typy, co powoduje niezgodność typu dla auto lambda = condition ? lambda1 : lambda2;
Poniższe działałoby:
auto lambda = condition ? std::function<void(T&)>(lambda1): std::function<void(T&)>(lambda2);
Aby podkreślić, że oba lambdy są rzeczywiście różnymi typami, możemy użyć <typeinfo>standardowej biblioteki i typeidoperatora. Lambda nie są typami polimorficznymi, więc standard gwarantuje, że operator „typeid” jest oceniany w czasie kompilacji. To pokazuje, że następujący przykład jest prawidłowy, nawet jeśli RTTI jest wyłączony:
Całkowity błąd to „błąd: operandy na?: Mają różne typy 'f (const std :: vector <int> &, size_t, size_t) [with T = unsigned char; size_t = long unsigned int] :: <lambda (unsigned char & )> 'i' f (const std :: vector <int> &, size_t, size_t) [with T = unsigned char; size_t = long unsigned int] :: <lambda (unsigned char &)> '”, w których widzę identyczne wszystkie typy i formaty.
krowa
1
@cow Ponieważ lambda same w sobie mają tę samą sygnaturę, więc kompilator, aby ukryć szczegóły swojej implementacji i dać bardziej zrozumiały błąd, podaje lokalizację i podpis obu lambd, które są identyczne. Ale w końcu są one nadal interpretowane jako SomeCompilerGeneratedTypeName1iSomeCompilerGeneratedTypeName2
Xatyrian
1
@cow dodałem przykład, który podkreśla początek odpowiedzi, może okazać się interesujący
Xatyrian
12
Co ciekawe, jeśli lambdas są bez wychwytywania, +można zastosować lewę operatora :
auto lambda1 =[](int arg){...};auto lambda2 =[](int arg){...};auto lambda = condition ?+lambda1 :+lambda2;// This compiles!
lambda(2019);
Działa to, ponieważ +przekształci lambda we wskaźnik funkcji, a oba wskaźniki funkcji mają ten sam typ (coś podobnego void (*)(int)).
Z GCC i Clang (ale nie z MSVC) +można pominąć, lambda nadal będą konwertowane na wskaźniki funkcji.
Od 2 lambda ( lambda1i lambda2) 2 różne typy, ?:nie można stwierdzić rodzaj powrót do lambdaz lambda1i lambda2. Dzieje się tak, ponieważ tych 2 nie można wzajemnie zamieniać.
SomeCompilerGeneratedTypeName1
iSomeCompilerGeneratedTypeName2
Co ciekawe, jeśli lambdas są bez wychwytywania,
+
można zastosować lewę operatora :Działa to, ponieważ
+
przekształci lambda we wskaźnik funkcji, a oba wskaźniki funkcji mają ten sam typ (coś podobnegovoid (*)(int)
).Z GCC i Clang (ale nie z MSVC)
+
można pominąć, lambda nadal będą konwertowane na wskaźniki funkcji.źródło
Kompilator nie może zdecydować, jaki typ
auto
powinien być:ponieważ każda lambda ma inny i niepowtarzalny typ.
Jednym ze sposobów, który będzie działał, jest:
źródło
Nie kompiluje się, ponieważ każda lambda ma unikalny typ, dla którego nie ma wspólnego typu
?:
.Możesz je owinąć
std::function<void(T&)>
npźródło
Od 2 lambda (
lambda1
ilambda2
) 2 różne typy,?:
nie można stwierdzić rodzaj powrót dolambda
zlambda1
ilambda2
. Dzieje się tak, ponieważ tych 2 nie można wzajemnie zamieniać.źródło