Стандартно ли гарантировано, что лямбда без захвата будет пустой?

12

Я ищу способ идентифицировать пустые (без захвата) лямбды из других лямбд в функции шаблона. В настоящее время я использую C ++ 17, но мне тоже интересно узнать ответы на C ++ 20.

Мой код выглядит так:

template<typename T>
auto func(T lambda) {
    // The aguments of the lambdas are unknown

    if constexpr (/* is captureless */) {
        // do stuff
    }
}

Гарантируется ли стандартом C ++ (17 или 20), что лямбда без захвата, которая может быть преобразована в указатель на функцию, также сделает std::is_emptyyield true?

Возьмите этот код в качестве примера:

auto a = []{}; // captureless
auto b = [c = 'z']{}; // has captures

static_assert(sizeof(a) == sizeof(b)); // Both are the same size
static_assert(!std::is_empty_v<decltype(b)>); // It has a `c` member
static_assert(std::is_empty_v<decltype(a)>); // Passes. It is guaranteed?

Живой пример

Гийом Расико
источник
2
Если вы заботитесь только о нешаблонных лямбдах, вы можете использовать SFINAE для проверки правильности преобразования преобразования в указатель на функцию ( +lambda).
HolyBlackCat
@HolyBlackCat Я думал об этом, но, насколько я помню, MSVC не позволяет этого, поскольку они перегружают оператор преобразования.
Гийом Расикот
@GuillaumeRacicot MS предоставляет отдельный оператор преобразования для всех доступных соглашений о вызовах. Просто выберите один и попробуйте преобразовать лямбду в сопоставимый указатель на функцию, и проверьте, успешно это или нет.
Реми Лебо
+Кажется, здесь работает .
HolyBlackCat

Ответы:

13

Нет, фактически, стандарт явно разрешает лямбдам иметь размер, который не соответствует их объявлению. [expr.prim.lambda.closure] / 2 штатов

Тип замыкания объявляется в самой маленькой области блока, области класса или области пространства имен, которая содержит соответствующее лямбда-выражение. [Примечание: это определяет набор пространств имен и классов, связанных с типом замыкания ([basic.lookup.argdep]). Типы параметров лямбда-объявления не влияют на эти связанные пространства имен и классы. - примечание конца] Тип закрытия не является агрегатным типом. Реализация может определять тип замыкания иначе, чем описано ниже, при условии, что это не изменяет наблюдаемое поведение программы, кроме как путем изменения:

  • размер и / или выравнивание типа укупорки,

  • является ли тип замыкания тривиально копируемым ([class.prop]), или (2.3)

  • является ли тип замыкания классом стандартной компоновки ([class.prop]).

Реализация не должна добавлять членов ссылочного типа rvalue к типу замыкания.

акцент мой

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

NathanOliver
источник