Antworten:
Einzelne Lambdas werden vom Compiler in verschiedene Klassen übersetzt. Zum Beispiel entspricht die Definition von lambda1:
class SomeCompilerGeneratedTypeName {
public:
SomeCompilerGeneratedTypeName(...) { // Capture all the required variables here
}
void operator()(T& arg) const {
// ...
}
private:
// All the captured variables here ...
};
Daher werden vom Compiler zwei verschiedene Typen generiert, was zu einer Typinkompatibilität für führt auto lambda = condition ? lambda1 : lambda2;
Folgendes würde funktionieren:
auto lambda = condition ? std::function<void(T&)>(lambda1) : std::function<void(T&)>(lambda2);
Um hervorzuheben, dass beide Lambdas tatsächlich unterschiedliche Typen sind, können wir sie <typeinfo>
aus der Standardbibliothek und dem typeid
Operator verwenden. Lambdas sind keine polymorphen Typen, daher garantiert der Standard, dass der Operator 'typeid' zur Kompilierungszeit ausgewertet wird. Dies zeigt, dass das folgende Beispiel auch dann gültig ist, wenn RTTI deaktiviert ist:
#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;
}
Die Ausgabe des Programms ist (mit GCC 8.3, siehe auf Gobolt ):
Z4mainEUlRZ4mainE1TE_/7654536205164302515
Z4mainEUlRZ4mainE1TE0_/10614161759544824066
SomeCompilerGeneratedTypeName1
und interpretiertSomeCompilerGeneratedTypeName2
Seltsamerweise +
kann ein Operator- Trick angewendet werden , wenn Lambdas ohne Erfassung sind :
auto lambda1 = [](int arg) { ... };
auto lambda2 = [](int arg) { ... };
auto lambda = condition ? +lambda1 : +lambda2; // This compiles!
lambda(2019);
Das funktioniert, weil +
Lambda in einen Funktionszeiger konvertiert wird und beide Funktionszeiger denselben Typ haben (so etwas wie void (*)(int)
).
Wenn GCC und Clang (aber nicht MSVC) +
weggelassen werden können, werden Lambdas weiterhin in Funktionszeiger konvertiert.
Der Compiler kann nicht entscheiden, welcher Typ auto
sein soll:
auto lambda = condition ? lambda1 : lambda2;
da jedes Lambda einen anderen und einzigartigen Typ hat.
Ein Weg, der funktionieren wird, ist:
auto lambda = [&](T& arg) {
return (condition ? lambda1(arg) : lambda2(arg));
}
Es wird nicht kompiliert, da jedes Lambda einen eindeutigen Typ hat. Es gibt keinen gemeinsamen Typ für ?:
.
Sie könnten sie einwickeln std::function<void(T&)>
, z
auto lamba1 = [&](T& arg) {
...
};
auto lambda2 = [&](T& arg) {
...
};
auto lambda = condition ? std::function(lambda1) : lambda2; // C++17 class template deduction