使用lambdas做嵌套函数

时间:2012-02-24 22:18:14

标签: c++ c++11

对于使用lambdas在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();
}

来自通过使用Pascal学习如何编码的一代,嵌套函数对我来说非常有意义。但是,在代码审查期间,这种用法似乎让我工作组中一些经验不足的开发人员感到困惑,因为我以这种方式使用了lambdas。

5 个答案:

答案 0 :(得分:11)

我没有看到嵌套函数本身有什么问题。我将lambdas用于嵌套函数,但前提是它满足某些条件:

  • 不止一次叫它。 (如果代码不太长,则直接编写代码)
  • 它实际上是一个内部函数,因此在任何其他上下文中调用它都没有意义。
  • 它足够短(最多10行)。

所以在你的例子中,我不会将lambdas用作第一个原因。

从概念上讲,嵌套函数可能与类中私有方法有用的原因相同。它们强制执行封装,并且可以更轻松地查看程序的结构。如果函数是某个其他函数的实现细节,那么为什么不明确地这样做呢?

我看到的最大问题是可读性;读取具有大量嵌套和缩进的代码更加困难。此外,人们对lambdas不太满意,但预计会有抵抗力。

答案 1 :(得分:6)

对于任何给定的代码,请将视为必要可见尽可能隐藏

  • 如果只在一个地方使用了这段代码,请将其写在那里。
  • 如果在同一函数内的多个位置使用它,则通过lambdas模拟嵌套函数。
  • 如果它被多个功能使用,请将其置于适当的功能中。

答案 2 :(得分:3)

您已经可以通过收到的评论猜测您正在做一些非正统的事情。这是C ++声名狼借的原因之一,人们永远不会停止滥用它。 Lambdas主要用作标准库算法的内联函数对象和需要某种回调机制的场所。我认为这涵盖了99%的用例,它应该保持这种状态!

正如Bjarne在他的一个讲座中所说:“并非一切都应该是一个模板,而不是一切都应该是一个对象。”

并非一切都应该是一个lambda :)自由站功能没有任何问题。

答案 3 :(得分:1)

在我看来它没用,但你可以只使用C ++ 03来实现它

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

但是再一次,恕我直言,这是没用的。

答案 4 :(得分:1)

这是一个非常有限的用例。对于初学者来说,必须在封闭函数内的几个点处需要本地函数中存在的功能,这样得到的局部重构将是可读性的胜利。否则,我将内联编写功能,如果有帮助,可能会把它放在一个块中。

但与此同时,功能必须是本地的或特定的,以至于我没有动机重构(不是那样)封闭功能的外部的功能,我可能也许在某些时候将它完全重用于另一个函数。它也必须简短:否则我只是将其移出,也许将它放在一个匿名命名空间(或标题中的namespace detail)或其他一些。交易地点以支持紧凑性并不需要太多时间(长期功能很难回顾)。

请注意,上述内容与语言无关。我不认为C ++会对此产生特别的影响。但是,如果有一个特定的C ++建议我必须就该主题给出,那就是我将使用默认的引用捕获([&])禁止。在没有仔细检查整个身体的情况下,无法判断该特定lambda表达式是否描述了闭包或局部函数。如果不是因为by-reference捕获([&][&foo])允许突变即使lambda没有被标记,那也不会那么糟糕(并不是那个闭包是'可怕的')可变和按值捕获([=][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