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

Дают ли 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 предоставлены очень интересные ссылки в комментариях которые иллюстрируют силу общих лямбд:

0

Решение

Для неуниверсальных лямбда-выражений 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 };, Между прочим, это означает, что мы должны дать классу и его оператору некоторую форму связи, в то время как локальные определения функций не имеют связи.

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

3

Другие решения

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

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

Я думаю, что это вопрос того, как вы используете лямбду. Как небольшая локальная функция закрытия, которую вы используете для улучшения обработки с помощью функций, которые принимают объекты функций в качестве параметров (например, 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; });
}

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

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

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

1

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

Да.

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

Да.

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

0
По вопросам рекламы [email protected]