C++ ternary assignment of lambda

Curiously, if lambdas are capture-less, operator + trick can be employed:

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

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

This works, because + will convert lambda into a function pointer, and both function pointers have the same type (something like void (*)(int)).

With GCC and Clang (but not with MSVC), + can be omitted, lambdas will still be converted into function pointers.


Individual lambdas are translated to different classes by the compiler. For example, lambda1's definition is equivalent to:

class SomeCompilerGeneratedTypeName {
public:
  SomeCompilerGeneratedTypeName(...) { // Capture all the required variables here
  }

  void operator()(T& arg) const {
    // ...
  }

private:
  // All the captured variables here ...
};

Therefore, two different types are generated by the compiler, which causes a type incompatibility for auto lambda = condition ? lambda1 : lambda2;

The following would work:

auto lambda = condition ? std::function<void(T&)>(lambda1) : std::function<void(T&)>(lambda2);

To highlight that both lambdas are indeed different types, we can use <typeinfo> from the standard library and the typeid operator. Lambdas are not polymorphic types, so the standard guarantees that the 'typeid' operator is evaluated at compile time. This shows that the following example is valid even if RTTI is disabled:

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

The output of the program is (with GCC 8.3, see on Gobolt):

Z4mainEUlRZ4mainE1TE_/7654536205164302515
Z4mainEUlRZ4mainE1TE0_/10614161759544824066