Atribuição ternária em C++ de lambda




c++11 conditional-operator (4)

Alguma idéia de por que o snippet a seguir não é compilado? Ele reclama com o erro "error: operandos to?: Have different types"

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

Como 2 lambdas ( lambda1 e lambda2 ) são 2 tipos diferentes, ?: Não é possível deduzir o tipo de retorno para lambda de lambda1 e lambda2 . Isso acontece porque esses 2 não são conversíveis entre si.


Curiosamente, se as lambdas são sem captura, o operador + truque pode ser empregado:

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

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

Isso funciona, porque + converterá lambda em um ponteiro de função e os dois ponteiros de função terão o mesmo tipo (algo como void (*)(int) ).

Com GCC e Clang (mas não com MSVC), + pode ser omitido, lambdas ainda serão convertidos em ponteiros de função.


Lambda individual é traduzido para diferentes classes pelo compilador. A definição de lambda1 é equivalente a:

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

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

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

Portanto, dois tipos diferentes são gerados pelo compilador, portanto, uma incompatibilidade de tipo para auto lambda = condition ? lambda1 : lambda2; auto lambda = condition ? lambda1 : lambda2;

O seguinte funcionaria:

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

Para destacar que as duas lambdas são realmente tipos diferentes, podemos usar <typeinfo> da biblioteca padrão e do operador typeid . Como as lambdas não são tipos polimórficos, o padrão garante que o operador typeid seja avaliado em tempo de compilação; portanto, o exemplo a seguir é válido mesmo se o RTTI estiver desativado.

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

A saída do programa é (com o GCC 8.3, veja no Gobolt ):

Z4mainEUlRZ4mainE1TE_/7654536205164302515
Z4mainEUlRZ4mainE1TE0_/10614161759544824066

O compilador não pode decidir que tipo de auto deve ser:

auto lambda = condition ? lambda1 : lambda2;

já que todo lambda tem um tipo diferente e único.

Uma maneira de funcionar é:

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






conditional-operator