C++ ternäre Zuordnung von Lambda




c++11 conditional-operator (4)

Da es sich bei 2 lambda1 ( lambda1 und lambda2 ) um 2 verschiedene Typen handelt, kann aus lambda1 und lambda1 kein Rückgabetyp für lambda lambda1 . Dies geschieht, weil diese 2 nicht ineinander konvertierbar sind.

Irgendeine Idee, warum das folgende Snippet nicht kompiliert wird? Es klagt mit einem Fehler "error: operands to?: Have different types"

  auto lambda1 = [&](T& arg) {
      ...
  };
  auto lambda2 = [&](T& arg) {
      ...
  };
  auto lambda = condition ? lambda1 : lambda2;

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));
}

Individuelles Lambda wird vom Compiler in verschiedene Klassen übersetzt. lambda1 's Definition entspricht:

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 erzeugt, also eine Typinkompatibilität für auto lambda = condition ? lambda1 : lambda2; 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 verschiedene Typen sind, können wir <typeinfo> aus der Standardbibliothek und den Operator typeid . Da Lambdas keine polymorphen Typen sind, garantiert der Standard, dass der Operator typeid zur Kompilierungszeit ausgewertet wird, sodass 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

Interessanterweise kann bei Lambdas ohne Capture Operator + Trick verwendet werden:

auto lambda1 = [](int arg) { ... };
auto lambda2 = [](int arg) { ... };

auto lambda = condition ? +lambda1 : +lambda2; // This compiles!
lambda(2019); 

Dies funktioniert, da + Lambda in einen Funktionszeiger konvertiert und beide Funktionszeiger den gleichen Typ haben (so etwas wie void (*)(int) ).

Bei GCC und Clang (aber nicht bei MSVC) kann + weggelassen werden, Lambdas werden weiterhin in Funktionszeiger konvertiert.







conditional-operator