C ++ троичное назначение лямбда - PullRequest
9 голосов
/ 06 ноября 2019

Есть идеи, почему следующий фрагмент не компилируется? Он жалуется с ошибкой «ошибка: операнды к?: Имеют разные типы»

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

Ответы [ 5 ]

12 голосов
/ 06 ноября 2019

Любопытно, что если лямбды не захватываются, можно использовать трюк оператора +:

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

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

Это работает, потому что + преобразует лямбду в указатель на функцию, и оба указателя на функцию имеюттот же тип (что-то вроде void (*)(int)).

С GCC и Clang (но не с MSVC), + может быть опущен, лямбда-выражения все равно будут преобразованы в указатели функций.

11 голосов
/ 06 ноября 2019

Индивидуальные лямбда переводятся компилятором в разные классы. Определение lambda1 эквивалентно:

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

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

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

Следовательно, компилятор генерирует два различных типа, таким образом несовместимость типов для auto lambda = condition ? lambda1 : lambda2;

Следующее будет работать:

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

Чтобы подчеркнуть, что обе лямбды действительно являются разными типами, мы можем использовать <typeinfo> из стандартной библиотеки и оператор typeid. Поскольку лямбда-выражения не являются полиморфными типами, стандарт гарантирует, что оператор typeid вычисляется во время компиляции, поэтому приведенный ниже пример действителен, даже если RTTI отключен.

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

Вывод программыесть (с GCC 8.3, см. на Gobolt ):

Z4mainEUlRZ4mainE1TE_/7654536205164302515
Z4mainEUlRZ4mainE1TE0_/10614161759544824066
10 голосов
/ 06 ноября 2019

Компилятор не может решить, какой тип auto должен быть:

auto lambda = condition ? lambda1 : lambda2;

, так как каждая лямбда имеет свой уникальный и уникальный тип.

Один способ, который будет работать:

auto lambda = [&](T& arg) {
     return (condition ? lambda1(arg) : lambda2(arg));
}
8 голосов
/ 06 ноября 2019

Он не компилируется, потому что у каждой лямбды есть уникальный тип, для ?: нет общего типа.

Вы можете обернуть их в std::function<void(T&)>, например,

auto lamba1 = [&](T& arg) {
  ...
};
auto lambda2 = [&](T& arg) {
  ...
};
auto lambda = condition ? std::function(lambda1) : lambda2; // C++17 class template deduction
8 голосов
/ 06 ноября 2019

Поскольку 2 лямбда-выражения (lambda1 и lambda2) - это два разных типа, ?: не может определить тип возврата для lambda из lambda1 и lambda2. Это происходит потому, что эти 2 не могут быть преобразованы друг в друга.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...