Использование лямбда-выражения для выполнения вложенных функций - PullRequest
12 голосов
/ 25 февраля 2012

Каково все мнение об использовании лямбда-выражения для выполнения вложенных функций в C ++?Например, вместо этого:

static void prepare_eggs()
{
   ...
}

static void prepare_ham()
{
   ...
}

static void prepare_cheese()
{
   ...
}

static fry_ingredients()
{
   ...
}

void make_omlette()
{
    prepare_eggs();
    prepare_ham();
    prepare_cheese();
    fry_ingredients();
}

Вы делаете это:

void make_omlette()
{
    auto prepare_eggs = [&]()
    {
       ...
    };

    auto prepare_ham = [&]()
    {
       ...
    };

    auto prepare_cheese = [&]()
    {
       ...
    };

    auto fry_ingredients = [&]()
    {
       ...
    };


    prepare_eggs();
    prepare_ham();
    prepare_cheese();
    fry_ingredients();
}

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

Ответы [ 5 ]

11 голосов
/ 25 февраля 2012

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

  • Он вызывается более одного раза.(в противном случае просто напишите код напрямую, если он не слишком длинный)
  • Это действительно внутренняя функция, так что вызывать ее в любом другом контексте не имеет смысла.
  • Это достаточно коротко (возможно, максимум 10 строк).

Так что в вашем примере я бы не использовал лямбда-выражения для причины номер один.

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

Самая большая проблема, которую я вижу, - это читаемость;труднее читать код, который имеет много вложений и отступов.Кроме того, людям не очень комфортно с лямбдами, поэтому ожидается сопротивление.

6 голосов
/ 26 февраля 2012

Для любого данного фрагмента кода, сделайте его настолько видимым, насколько необходимо и максимально скрытым :

  • Если фрагмент кода используется только в одном месте, напишите его там.
  • Если он используется в нескольких местах одной и той же функции, эмулируйте вложенные функции через лямбды.
  • Если он используется несколькими функциями, поместите его в правильную функцию.
3 голосов
/ 25 февраля 2012

По комментариям, которые вы получили, вы уже можете догадаться, что делаете что-то неортодоксальное.Это одна из причин плохой репутации C ++, люди никогда не прекращают злоупотреблять ею.Лямбды в основном используются как объекты встроенных функций для стандартных библиотечных алгоритмов и мест, которые требуют какого-то механизма обратного вызова.Я думаю, что это покрывает 99% сценариев использования, и так должно быть!

Как сказал Бьярн в одной из своих лекций: «Не все должно быть шаблоном, и не все должно быть объектом».

И не все должно быть лямбда :) нет ничего плохого в свободностоящей функции.

1 голос
/ 26 февраля 2012

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

Но в то же время, функциональность должна быть локальной или достаточно специфичной, чтобы у меня не было стимула для рефакторинга функциональности за пределами (не так) включающей функции, где я мог бы, возможно, повторно использовать ее полностью в другой функции в какой-то момент.Оно также должно быть коротким: в противном случае я просто собираюсь его убрать, возможно, поместив в анонимное пространство имен (или namespace detail в заголовке) или что-то подобное.Мне не нужно много, чтобы поменять местность в пользу компактности (длинные функции - трудная задача для обзора).

Обратите внимание, что вышесказанное строго не зависит от языка.Я не думаю, что C ++ крутится на этом.Если есть один конкретный совет C ++, который я должен дать по этой теме, это то, что я бы запретил использование захвата по умолчанию по ссылке ([&]).Не было бы никакого способа определить, описывает ли это конкретное лямбда-выражение замыкание или локальную функцию без тщательного изучения всего тела .Что было бы не так уж плохо (это не значит, что замыкания «страшны»), если бы не тот факт, что эти перехваты ([&], [&foo]) допускают мутации, даже если лямбда не помечена как изменяемая, иЗахваты со значениями ([=], [foo]) могут сделать нежелательную копию или даже попытаться сделать невозможную копию для типов только для перемещения.В общем, я бы предпочел не захватывать что-либо вообще, если это возможно (это то, для чего нужны параметры!), И использовать отдельные захваты при необходимости.Это особенно проблематично

Подводя итог:

// foo is expensive to copy, but ubiquitous enough
// that capturing it rather than passing it as a parameter
// is acceptable
auto const& foo_view = foo;
auto do_quux = [&foo_view](arg_type0 arg0, arg_type1 arg1) -> quux_type
{
    auto b = baz(foo_view, arg0, arg1);
    b.frobnicate;
    return foo_view.quux(b);
};

// use do_quux several times later
1 голос
/ 25 февраля 2012

На мой взгляд, это бесполезно, но вы можете достичь этого, используя только C ++ 03

void foo() {
  struct {
    void operator()() {}
  } bar;
  bar();
}

Но еще раз, ИМХО, это бесполезно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...