Почему [=] {} имеет лямбда-захват? - PullRequest
0 голосов
/ 29 ноября 2018

На интуитивном уровне имеет смысл, что лямбда, которая не должна нести никакого состояния (посредством ссылки или иным образом), должна быть чисто конвертируемой в пустой указатель на функцию.Однако недавно я был удивлен, увидев следующее сбой в GCC, Clang и MSVC:

int main(int, char *[]) {
    void (*fp)() = []{}; // OK
  //fp = [=]{};          // XXX - no user defined conversion operator available
  //fp = [&]{};          // XXX - same ...
}

Спецификация C ++ 17 (или, по крайней мере, видимая публичная версия черновика N4713 ), относится в пункте 7 § 8.4.5.1 [expr.prim.lambda.closure] к лямбдам с захватами и без них:

Тип закрытия для неуниверсальной лямбды-expression без лямбда-захвата , чьи ограничения (если таковые имеются) выполнены, имеет функцию преобразования в указатель на функцию со связью языка C ++ (10.5), имеющую тот же параметр и возвращаемые типы, что и оператор вызова функции типа замыкания,...

Однако, изучая формальную грамматику, вы можете увидеть следующее в § 8.4.5 [expr.prim.lambda] :

  • лямбда-выражение :
    • лямбда-выражение составное выражение
    • ...
  • лямбда-интродьюсер :
    • [ лямбда-захват opt ]
  • ...

и в § 8.4.5.2 [expr.prim.lambda.capture] :

  • лямбда-захват :
    • захват по умолчанию
    • список захвата
    • захват по умолчанию, список захвата
  • захват по умолчанию :
    • &
    • =

Таким образом, все компиляторы фактически подчинялись букве йЗакон к моему ужасу ...

Почему язык определяет существование захвата как узкое грамматическое различие в объявлении вместо того, чтобы основывать его на том, содержит ли тело ссылки на любое нестатическое / захваченное состояние

Ответы [ 2 ]

0 голосов
/ 29 ноября 2018

Правило, которое вы предлагаете, было бы чрезвычайно хрупким, особенно в мире, предшествующем P0588R1, когда неявные захваты зависели от использования odr.

Рассмотрим:

void g(int);
void h(const int&);

int my_min(int, int);

void f(int i) {
    const int x = 1, y = i;
    [=]{ g(x); }; // no capture, can convert?
    [=]{ g(y); }; // captures y
    [=]{ h(x); }; // captures x
    [=]{ my_min(x, 0); }; // no capture, can convert?
    [=]{ std::min(x, 0); }; // captures x
}
0 голосов
/ 29 ноября 2018

Изменение, которое позволило преобразование, было инициировано комментарием национального органа.См. n3052: преобразование лямбда-выражений в функциональные указатели , относящиеся к национальному телу комментарий UK 42 :

Лямбда с пустым списком захвата имеет идентичную семантику дляобычный тип функции.Требуя этого сопоставления, мы получаем эффективный лямбда-тип с известным API, который также совместим с существующей операционной системой и функциями библиотеки C.

и разрешение от N3052 было:

Разрешение: Добавить новый абзац: «Лямбда-выражение с пустым набором захвата должно быть преобразовано в указатель на функциютип R (P), где R - тип возвращаемого значения, а P - список параметров-типов лямбда-выражения. "Кроме того, было бы хорошо (а) разрешить преобразование в ссылку на функцию и (б) разрешить внешние типы указателей на функции "С".

...

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

Тип замыкания для лямбда-выражения без лямбда-захвата имеет открытую не виртуальную неявную функцию преобразования констант в указатель на функцию, имеющую тот же параметр и типы возврата, что и при вызове функции типа замыканияоператор.Значение, возвращаемое этой функцией преобразования, должно быть адресом функции, которая при вызове имеет тот же эффект, что и вызов оператора вызова функции типа замыкания.

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

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

...