Как общая лямбда работает в C ++ 14?

114

Как работает универсальная лямбда ( autoключевое слово как тип аргумента) в стандарте C ++ 14?

Основан ли он на шаблонах C ++, где для каждого другого типа аргумента компилятор генерирует новую функцию с тем же телом, но замененными типами (полиморфизм во время компиляции), или он больше похож на дженерики Java (стирание типов)?

Пример кода:

auto glambda = [](auto a) { return a; };
sasha.sochka
источник
6
Исправлено для C ++ 14, изначально использовался рассматриваемый C ++ 11
sasha.sochka 08

Ответы:

130

Общие лямбды были введены в C++14.

Проще говоря, тип замыкания, определенный лямбда-выражением, будет иметь шаблонный оператор вызова, а не обычный, не шаблонный оператор вызова лямбда-выражений C++11(конечно, когда он autoхотя бы один раз появляется в списке параметров).

Итак, ваш пример:

auto glambda = [] (auto a) { return a; };

Сделаем glambdaэкземпляр такого типа:

class /* unnamed */
{
public:
    template<typename T>
    T operator () (T a) const { return a; }
};

В параграфе 5.1.2 / 5 стандартного проекта C ++ 14 n3690 указывается, как определяется оператор вызова типа закрытия данного лямбда-выражения:

Тип закрытия для неуниверсального лямбда-выражения имеет общедоступный встроенный оператор вызова функции (13.5.4), параметры и возвращаемый тип которого описываются предложением-объявлением-параметром лямбда-выражения и конечным-возвращаемым-типом соответственно. Для универсальной лямбда-выражения тип замыкания имеет общедоступный встроенный шаблон-член оператора вызова функции (14.5.2), чей список-параметров шаблона состоит из одного придуманного параметра-шаблона типа для каждого вхождения auto в предложении-объявлении параметра лямбда, в порядке появления. Шаблон-параметр изобретенного типа является пакетом параметров, если соответствующее объявление параметра объявляет пакет параметров функции (8.3.5). Тип возвращаемого значения и параметры функции шаблона оператора вызова функции являются производными от конечного-возвращаемого-типа и предложения-параметра лямбда-выражения путем замены каждого вхождения auto в описателях-объявлениях предложения-объявления-параметра на имя соответствующий придуманный шаблон-параметр.

В заключение:

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

Как поясняется в предыдущем абзаце, общие лямбды - это просто синтаксический сахар для уникальных безымянных функторов с шаблонным оператором вызова. Это должно ответить на ваш вопрос :)

Энди Проул
источник
7
Однако они также допускают локально определенный класс с помощью шаблонного метода. Что новенькое.
Yakk - Adam Nevraumont
2
@Yakk: Разве ограничение для локальных шаблонов функций не было снято с C ++ 11?
Себастьян Мах
2
@phresnel: Нет, это ограничение не снято
Энди Проул
1
@AndyProwl: Я понимаю свою ошибку. Что действительно было отменено, так это использование локальных типов в качестве аргументов шаблона (как в int main () { struct X {}; std::vector<X> x; })
Себастьян Мах
1
@phresnel: Верно, это действительно изменилось
Энди Проул
25

К сожалению , они не являются частью C ++ 11 ( http://ideone.com/NsqYuq ):

auto glambda = [](auto a) { return a; };

int main() {}

С g ++ 4.7:

prog.cpp:1:24: error: parameter declared auto
...

Однако способ его реализации в C ++ 14 в соответствии с предложением Портленда для общих лямбда-выражений :

[](const& x, & y){ return x + y; }

Это приведет по большей части к обычному созданию класса анонимного функтора, но при отсутствии типов компилятор будет выдавать шаблонный член -operator() :

struct anonymous
{
    template <typename T, typename U>
    auto operator()(T const& x, U& y) const -> decltype(x+y)
    { return x + y; }
};

Или в соответствии с новым предложением Proposal for Generic (Polymorphic) Lambda Expressions

auto L = [](const auto& x, auto& y){ return x + y; };

--->

struct /* anonymous */
{
    template <typename T, typename U>
    auto operator()(const T& x, U& y) const // N3386 Return type deduction
    { return x + y; }
} L;

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

Себастьян Мах
источник
6
Предложение об отказе от спецификатора типа совершенно гротескно.
Гонки легкости на орбите
Они вошли с g ++ - 4.9 . Вам нужно поставить -std=c++1y.
emsr
Я не понимал, что у ideone еще нет gcc-4.9 и C ++ 14.
emsr
вопрос: у этого autoесть те же правила удержания, что у классического авто? Если мы обратимся к шаблонной аналогии, это будет означать, что auto не является автоматическим, это те же правила, что и выведение типа шаблона. Тогда возникает вопрос: эквивалентно autoли выведение по шаблону ?
v.oddou
@ v.oddou: "Классический авто" - это хорошо. Для меня «классический авто» означает «Переменная стека», и когда-то использовалось в отличие от staticили register:) В любом случае, да, использование autoтам означает, что под капотом генерируется обычный шаблон. Фактически, лямбда-выражение будет заменено внутри компилятора классом функтора, а autoпараметр означает, что template <T> ... (T ...)он будет испущен.
Себастьян Мах
17

Это предлагаемая функция C ++ 14 (не в C ++ 11), аналогичная (или даже эквивалентная) шаблонам. Например, N3559 предоставляет следующий пример:

Например, это общее лямбда-выражение, содержащее оператор:

auto L = [](const auto& x, auto& y){ return x + y; };

может привести к созданию типа закрытия и объекта, который ведет себя аналогично структуре ниже:

struct /* anonymous */
{
    template <typename T, typename U>
    auto operator()(const T& x, U& y) const // N3386 Return type deduction
    { return x + y; }
} L;
Кассио Нери
источник