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

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

Является ли он основан на шаблонах С++, где для каждого другого компилятора типа аргумента генерируется новая функция с одним и тем же телом, но с заменой типов (полиморфизм времени компиляции) или он более похож на Java generics (тип erasure)?

Пример кода:

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

Ответ 1

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

Просто тип замыкания, определяемый выражением лямбда, будет иметь шаблонный оператор вызова, а не обычный, не шаблонный оператор вызова C++11 lambdas (конечно, когда 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 стандартного проекта С++ 14 n3690 определяет, как определяется оператор вызова типа замыкания данного лямбда-выражения:

Тип замыкания для неэквивалентного лямбда-выражения имеет открытый оператор вызова функции (13.5.4) параметры и тип возвращаемых данных описываются параметром-объявления-объявления-лямбда-выражения и trailing-return-type соответственно. Для общей лямбда тип закрытия имеет открытый встроенный вызов функции шаблон участника-члена (14.5.2), список шаблонов-параметров которого состоит из одного изобретенного типа шаблона-параметра для каждого вхождения auto в lambdas parameter-declaration-clause, в порядке появления. Параметр-шаблон изобретенного типа представляет собой пакет параметров, если декларация соответствующего параметра объявлена пакет параметров функций (8.3.5). Тип возврата и функциональные параметры вызова функции шаблон оператора выводятся из выражений лямбда-выражения trailing-return-type и parameter-declarationclause заменяя каждое вхождение auto в спецификаторах decl-parameter-declaration-clause с имя соответствующего изобретенного шаблона-параметра.

Наконец:

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

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

Ответ 2

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

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

int main() {}

С g++ 4.7:

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

Однако, как это может быть реализовано в С++ 14 в соответствии с предложением Portland для общих лямбда

[](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; }
};

Или согласно новому предложению Предложение для универсальных (полиморфных) выражений лямбда

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;

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

Ответ 3

Это предлагаемая функция С++ 14 (не в С++ 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;