Как компилятор обрабатывает лямбды иначе, чем обычные функции? - PullRequest
0 голосов
/ 27 января 2019

Интересно, как компилятор обрабатывает лямбда-функции в отличие от обычных функций.Даже за исключением списка захвата, как я думаю, он называется, он ведет себя немного по-другому.

Например, как использовалось в моем последнем посте, Попытка передать лямбду constexpr и использовать ее для явногоукажите возвращаемый тип , я использовал constexpr lambda и передал его как обычный параметр функции.Я цитирую часть ответа.

Параметры функций constexpr сами по себе не являются объектами constexpr, поэтому их нельзя использовать в константных выражениях.

template <typename Lambda_T>
constexpr static auto foo(Lambda_T l) {
    return std::array<event, (l())>{};
} 
// Compiles with GCC (C++17), though ill-formed (according to the answer of my last post)

Хотя,что бросается в глаза, это то, что это компилирует передачу constexpr lambda, но не компилирует передачу обычной (глобальной) функции constexpr.Что вызывает эту разницу?И есть ли другие различия в поведении компилятора между обычными функциями и лямбда-функциями?

Редактировать: Пример лямбда-реализации

foo([](){ return 4; }); // C++17 Lambda's are implicitly constexpr

Лямбда в основномоболочка для значения в этом случае.

Редактировать: Глобальная функция

Всякий раз, когда передается глобальная функция, компилятор будет жаловаться - в отличие от использования лямбда -что эту функцию, независимо от того, определена она как constexpr или нет, нельзя использовать в постоянном условии:

prog.cc:8:20: error: 'l' is not a constant expression

1 Ответ

0 голосов
/ 29 января 2019

Удаляя некоторые посторонние вещи из вашего фрагмента, мы получаем

template<typename T>
constexpr void foo(T t)
{
    constexpr int i = t();
}

constexpr int f() { return 42; }
auto l = []{ return 42; }

Примечания:

  1. Вы пытаетесь использовать t() как constexpr вfoo.foo на самом деле может быть нормальной функцией и при этом вести себя так же.

  2. Лямбда не является функцией.Это анонимная структура с operator().

    struct L { constexpr int operator()() const { return 42; } };
    

    T выводится как тип, эквивалентный L в вызове foo(l).

  3. T выводится как int(*)() при вызове foo(f).

  4. В обоих случаях t не является контрагентом внутри foo.


С [expr.const]

Выражение e является выражением основной константы, если только вычислениеe, следуя правилам абстрактной машины, будет вычислять одно из следующих выражений: [...]

  • преобразование lvalue в rvalue, если [...]

    • энергонезависимое значение, которое относится к энергонезависимому объекту, определенному с помощью constexpr, или которое относится к неизменяемому подобъекту такого объекта, или

    • энергонезависимое glvalue литерального типа, которое относится к энергонезависимому объекту, время жизни которого началось в пределах оценки e;

Для вызова через int(*)() требуется преобразование lvalue в rvalue.t не является объектом, определенным с помощью constexpr, и при этом он не начал свою жизнь в рамках оценки t().Поэтому t() не является constexpr в foo(f).

Для вызова operator() не требуется преобразование lvalue в rvalue.Поскольку нет доступа к элементу, это просто вызов функции constexpr.Следовательно, t() - это constexpr в foo(l).

Существует еще один шаг от основных константных выражений до константных выражений, но это не важно для обсужденияздесь.

...