Odpowiedzi:
Poszczególne lambdy są tłumaczone przez kompilator na różne klasy. Na przykład definicja lambda1 jest równoważna z:
class SomeCompilerGeneratedTypeName {
public:
SomeCompilerGeneratedTypeName(...) { // Capture all the required variables here
}
void operator()(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 typeid
operatora. 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:
#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;
}
Dane wyjściowe programu to (z GCC 8.3, patrz Gobolt ):
Z4mainEUlRZ4mainE1TE_/7654536205164302515
Z4mainEUlRZ4mainE1TE0_/10614161759544824066
SomeCompilerGeneratedTypeName1
iSomeCompilerGeneratedTypeName2
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.
Kompilator nie może zdecydować, jaki typ auto
powinien być:
auto lambda = condition ? lambda1 : lambda2;
ponieważ każda lambda ma inny i niepowtarzalny typ.
Jednym ze sposobów, który będzie działał, jest:
auto lambda = [&](T& arg) {
return (condition ? lambda1(arg) : lambda2(arg));
}
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
auto lamba1 = [&](T& arg) {
...
};
auto lambda2 = [&](T& arg) {
...
};
auto lambda = condition ? std::function(lambda1) : lambda2; // C++17 class template deduction