На интуитивном уровне имеет смысл, что лямбда, которая должна нести какое-либо состояние (через ссылку или иначе), должна быть чистой конвертируемой в указатель голой функции. Тем не менее, я был недавно удивлен, увидев следующие сбои в 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] к лямбдам с захватами и без них:
Тип замыкания для не-общего лямбда-выражения без лямбда-захвата, ограничения которого (если они есть) удовлетворяются, имеет функцию преобразования для указателя на функцию с C++ языковой связью (10.5), имеющей те же параметры и возвращаемые типы, что и замыкание функции вызова функции....
Однако, изучая формальную грамматику, вы можете увидеть следующее в § 8.4.5 [expr.prim.lambda]:
- лямбда-выражение:
- составная заявка лямбда-интродуктора
- ...
- лямбда-интродуктор:
- [ lambda-capture opt ]
- ...
и в § 8.4.5.2 [expr.prim.lambda.capture]:
- лямбда-захват:
- Захват по умолчанию
- Захват-лист
- capture-default, capture-list
- capture-default:
- &
- знак равно
Так что все компиляторы на самом деле подчинялись букве закона до ужаса...
Почему язык определяет существование захвата как узкое грамматическое различие в декларации, а не основывает его на том, содержит ли тело ссылки на какое-либо нестатическое/захваченное состояние?