Почему лямбда-выражения не допускаются в неоцененных операндах, но допускаются в неоцененных частях константных выражений?
Если мы посмотрим на проект стандартного раздела C++ 5.1.2
Пункт 2 лямбда-выражений гласит (выделение мое в дальнейшем):
Оценка лямбда-выражения приводит к временному значению prvalue (12.2). Этот временный объект называется объектом закрытия. Лямбда-выражение не должно появляться в неоцененном операнде (пункт 5). [Примечание: закрывающий объект ведет себя как функциональный объект (20.8). - примечание конца]
и раздел 5.19
Постоянное выражение параграфа 2 гласит:
Условное выражение является основным константным выражением, если оно не включает одно из следующего в качестве потенциально вычисляемого подвыражения (3.2), но подвыражения логических операций И (5.14), логического ИЛИ (5.15) и условных (5.16) операций, которые не оцениваются не считаются [...]
и имеет следующую пулю:
- лямбда-выражение (5.1.2);
Так почему же лямбда-выражения не допускаются в неоцененном операнде, но допускаются в неоцененных частях константных выражений?
Я могу видеть, как для неоцененных операндов информация о типах в нескольких случаях ( decltype или typeid) не очень полезна, так как каждая лямбда имеет уникальный тип. Хотя почему мы хотели бы разрешить их в неоцененном контексте константного выражения, неясно, возможно, чтобы разрешить SFINAE?
1 ответ
Основная причина исключения неоцененных операндов описана в стандартных отчетах о дефектах основного языка C++ и принятых проблемах # 1607. Лямбды в параметрах шаблона, которые пытаются прояснить это ограничение и указывают намерение ограничения в разделе 5.1.2
было:
[...] предотвращение необходимости иметь дело с ними в сигнатурах шаблонов функций [...]
Так как проблема выпускается, текущая формулировка фактически имеет дыру, так как константные выражения позволяют их в неоцененном контексте. Но это не дает прямого объяснения этому ограничению. Выражает желание избежать искажения имен, и вы можете сделать вывод, что также было нежелательно избегать расширения SFINAE, поскольку предлагаемая резолюция стремится ужесточить ограничение, даже если несколько жизнеспособных альтернатив позволили бы SFINAE. Модифицированная версия 5.1.2
пункт 2 следующим образом:
Лямбда-выражение не должно появляться в неоцененном операнде (пункт 5 [expr]), в аргументе шаблона, в объявлении псевдонима, в объявлении typedef или в объявлении функции или шаблона функции вне тела функции. и аргументы по умолчанию [Примечание: цель состоит в том, чтобы не допустить появления лямбда-выражений в подписи - примечание] [Примечание: закрывающий объект ведет себя как функциональный объект (20.10 [function.objects]). —Конечная записка]
Это предложение было принято и находится в N3936
( см. этот ответ для ссылки)
Для более ясного обсуждения логического обоснования, чтобы избежать использования лямбда-выражений в качестве неоцененного операнда. Дискуссия под названием " Обоснование лямбда-выражений, недопустимых в недооцененных контекстах на comp.lang.cpp.moderated" Даниэля Крюглера приводит три причины:
Экстремальное расширение возможных случаев SFINAE:
[...] Причина, по которой они были исключены, была вызвана именно этим экстремальным расширением случаев sfinae (вы открывали ящик Pandora для компилятора)[...]
Во многих случаях это просто бесполезно, поскольку каждая лямбда имеет уникальный тип, приведенный гипотетический пример:
template<typename T, typename U> void g(T, U, decltype([](T x, T y) { return x + y; }) func); g(1, 2, [](int x, int y) { return x + y; });
Тип лямбда- выражения в объявлении и вызове различен (по определению), и поэтому это может не сработать.
Перенос имен также становится проблемой, так как после того, как вы разрешите лямбду в сигнатуре функции, придется также искажать тела лямбды. Это означает, что нужно придумать правила для манипулирования каждым возможным оператором, что обременительно по крайней мере для некоторых реализаций.