Дают ли 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 предоставлены очень интересные ссылки в комментариях которые иллюстрируют силу общих лямбд:
Boost.Hana: Гетерогенные комбинаторы для экспрессивного метапрограммированияGitHub)
Для неуниверсальных лямбда-выражений 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 универсальные лямбды реальным улучшением языка
Да.
или это разновидность синтаксического сахара?
Да.
Вы, кажется, подразумеваете, что синтаксический сахар не является реальным улучшением языка. Имейте в виду, что сам язык является синтаксическим сахаром. Вы могли бы писать все в машинном коде 🙂