Общие лямбды: синтаксический сахар или нет?

Дают ли C++14 общие лямбды реальное улучшение языка, или они являются своего рода синтаксическим сахаром? Есть ли ситуации, когда

[](auto param1, auto param2, /* ... */ auto paramN)
{
    return /* ... */;
}

не может быть заменено на

template <typename Type1, typename Type2, /* ... */ typename TypeN>
auto foo(Type1&& param1, Type2&& param2, /* ... */ TypeN&& paramN)
{
    return  /* ... */;
}

или же

struct bar
{
    template <typename Type1, typename Type2, /* ... */ typename TypeN>
    auto operator()(Type1&& param1, Type2&& param2, /* ... */ TypeN&& paramN)
    {
        return  /* ... */;
    }
};

?


Kerrek SB предоставил очень интересные ссылки в комментариях, которые иллюстрируют силу общих лямбд:

3 ответа

Решение

Для неуниверсальных лямбда-выражений C++11 существует несколько простой перевод, который можно выполнить:

void foo()
{
    int i = 42; int j = 22;
    auto f = [i, &j](int k) { return i + j + k };
    // proceed to use f
 }

Например:

void foo()
{
    int i = 42; int j = 22;
    struct {
        int i; int& j;
        // can't deduce return type
        int operator()(int k) const
        { return i + j + k; }
    } f { i, j };
    // proceed to use f
}

Для общих лямбда-выражений C++14 это не так просто. Предположим, на этот раз мы используем auto f = [i, &j](auto k) { return i + j + k; }, Затем мы должны произвести следующий оператор вызова:

template<typename T>
auto operator()(T k) const { return i + j + k; }

Проблема в том, что мы не можем определить шаблон в области действия функции (ограничение, также известное как отсутствие локальных шаблонов). Таким образом, мы должны переместить определение типа замыкания из включающей функции в область пространства имен (давая ему имя в процессе), а затем использовать closure_type f { i, j };, Между прочим, это означает, что мы должны дать классу и его оператору некоторую форму связи, в то время как локальные определения функций не имеют связи.

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

На лямбдах вообще:

Некоторые считают это "действительно опрятным!"; другие видят в этом способ написания опасно неясного кода. ИМО, оба правы. --- Бьярне Страуструп

Я думаю, что это вопрос того, как вы используете лямбду. Как небольшая локальная функция закрытия, которую вы используете для улучшения обработки с помощью функций, которые принимают объекты функций в качестве параметров (например, std::sort), Я на самом деле не видел пример, где общие лямбды добавили бы какую-либо выгоду.

Если вы используете их для написания кода, подобного haskell, в C++, это добавит некоторую выгоду, но я видел слишком много примеров кода, где время жизни объекта хотя бы частично игнорировалось. Поэтому я не думаю, что это принесет пользу.

Позвольте мне объяснить это немного:

void foo(std::vector<int>& v)
{
    std::sort(v.begin(), v.end(), [](int i, int j) { return (i < j); });
}

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

void C::foo()
{
    registerLazyMultithreadedCallback([=this]() { return this; });
}

Это усложнит ситуацию, потому что вы должны убедиться, что возвращаемый вами объект действителен. Это может привести к нелепым ситуациям (например, звонить через короткое время после разрушения). По моему опыту, кодер дважды подумает, прежде чем писать такую ​​конструкцию без лямбды.

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

Я думаю, что я один из тех, кто думает, что вы можете написать опасный неясный код с помощью лямбды.

Делают ли C++14 универсальные лямбды реальным улучшением языка

Да.

или это разновидность синтаксического сахара?

Да.

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

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