Инициализация захвата лямбда в тройном операторе

Я хотел создать лямбда следующим образом:

auto l1 = condition ?
          [](){ return true; } :
          [number](){ return number == 123; };

Однако я получил ошибку:

operands to ?: have different types ‘main()::<lambda()>’ and ‘main()::<lambda()>’

Очевидно, что типы кажутся одинаковыми. Я думал, что захват number только в одном из lambdas может быть проблемой, но я получаю для них ту же ошибку:

//check if capturing number in both lambdas would help
auto l2 = condition ?
          [number](){ return true; } :
          [number](){ return number == 123; };
//maybe the first lambda capture was optimised out? let make sure:
auto l3 = condition ?
          [number](){ return number != 123; } :
          [number](){ return number == 123; };

Я знаю, что могу сделать это иначе (ниже), но мне интересно, в чем причина такого поведения. Он был скомпилирован с включенным GCC6.3.1, С++ 14.

//compiles      
auto l4 = condition ?
          [](const int){ return true; } :
          [](const int number){ return number == 123; };

Ответы

Ответ 1

Каждое выражение лямбда имеет уникальный тип (т.е. тип закрытия, который является уникальным неназванным не-объединенным неагрегатным типом класса) даже с тем же сигнатурным и функциональным телом; компилятор просто не может вывести общий тип тернарный условный оператор для переменной, объявленной auto, два типа закрытия не имеют значения при все.

Вместо этого вы можете использовать std::function. например.

std::function<bool()> l1;
if (condition)
     l1 = [](){ return true; };
else
     l1 = [number](){ return number == 123; };

Для l4 обратите внимание, что лямбда-выражение с пустым списком захвата может быть неявно преобразовано в соответствующий указатель функции. В этом случае оба они могут быть преобразованы в один и тот же тип указателя функции (т.е. bool(*)(int)), который затем можно было бы вывести как общий тип тернарного условного оператора и тип l4.

Ответ 2

Если вы действительно хотите использовать условный оператор, вы можете сделать это следующим образом:

auto l1 = condition
          ? std::function<bool()>{[](){ return true; }}
          : std::function<bool()>{[number](){ return number == 123; }};

В С++ 17 это можно упростить благодаря Вывод аргумента шаблона шаблона:

auto l1 = condition
          ? std::function{[](){ return true; }}
          : std::function{[number](){ return number == 123; }};

Ответ 3

Лямбда в С++ - это экземпляр контракта-функтора, реализующего локальный класс. Я имею в виду operator() и т.д. И эти классы не связаны и имеют разные типы.

Ваш код эквивалентен

struct l1 {
  bool operator () const {
    return true;
  }
};

struct l2 {
  private int number_:
  l2(int number): number_(number){}

  bool operator () const {
    return number_ == 123;
  }
};

int number = ???
auto l3 = condition ? l1 : l2(number);