Почему лямбда-выражения не допускаются в неоцененных операндах, но допускаются в неоцененных частях константных выражений?

Если мы посмотрим на проект стандартного раздела 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" Даниэля Крюглера приводит три причины:

  1. Экстремальное расширение возможных случаев SFINAE:

    [...] Причина, по которой они были исключены, была вызвана именно этим экстремальным расширением случаев sfinae (вы открывали ящик Pandora для компилятора)[...]

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

    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; });
    

    Тип лямбда- выражения в объявлении и вызове различен (по определению), и поэтому это может не сработать.

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

Другие вопросы по тегам