7

作为一个愚蠢的例子,假设我有一个函数int f(vector<int> v),由于某种原因,我需要vf. 与其将辅助函数放在其他地方(这可能会增加混乱并损害可读性),不如做这样的事情有哪些优点和缺点(效率、可读性、可维护性等):

int f(vector<int> v)
{
    auto make_unique  = [](vector<int> &v)
    {
        sort(begin(v), end(v));
        auto unique_end = unique(begin(v), end(v));
        v.erase(unique_end, end(v));
    };
    auto print_vector = [](vector<int> const &v)
    {
        copy(begin(v), end(v), ostream_iterator<int>(cout, " "));
        cout << endl;
    };

   make_unique (v);
   print_vector(v);
   // And then the function uses these helpers a few more times to justify making
   // functions...
}

还是有一些首选的选择?

4

3 回答 3

7

这种局部作用域函数的优点是它们不会用“帮助”定义污染周围的代码——所有行为都可以限制在一个作用域内。而且由于它们可以访问周围函数的词法范围,因此它们可以用于在不传递许多参数的情况下对行为进行分解。

您还可以使用它们创建小型 DSL 来抽象函数的机械细节,以便您以后更改它们。您为重复值定义常量;为什么不对代码做同样的事情?

举个小例子,状态机:

vector<int> results;
int current;
enum { NORMAL, SPECIAL } state = NORMAL;

auto input = [&]{ return stream >> current; }
auto output = [&](int i) { results.push_back(i); };
auto normal = [&]{ state = NORMAL; };
auto special = [&]{ state = SPECIAL; };

while (input()) {
    switch (state) {
    case NORMAL:
        if (is_special(current)) special(); else output(current);
        break;
    case SPECIAL:
        if (is_normal(current)) normal();
        break;
    }
}

return results;

一个缺点是您可能会不必要地隐藏和专门化一个可能对其他定义有用的通用函数。一个uniquifyorprint_vector函数值得浮出并重用。

于 2013-11-12T04:05:35.497 回答
2

效率:
这基本上是函数与函子,这是 lambda 在引擎盖下的内容。函子实际上可以更快,这是因为它们更容易内联,并非所有编译器都不会内联函数指针(尽管有可能)。这样做的原因是,当您传递函数指针时,编译器只知道函数的类型,而仿函数拥有整个函数,因为类型是唯一的。

可读性:
这部分或多或少是基于意见的。函数指针 IMO 很烦人,它们的类型很丑,而且不像函子那样通用。例如,仿函数可以在运行时轻松传递。另一方面,写出一个完整的函数比一个大的 lambda 更具可读性。

我想说的最后一点是,像函子这样的 lambda 可以具有状态(与函数不同,除非您计算静态变量),使用 lambda 您可以捕获变量。由于编译器内联限制,最好不要传递函数指针。

所以我想说,对于传递给 stdlib 的小函数,最好使用 lambda,而对于较大的函数,实现一个仿函数,函数指针在 c++11 中真的不再是惯用的。

不幸的是,在我看来,您使用它们的方式并不好。编译器将它们作为普通函数内联是没有问题的。一个名为print_vectoror的函数make_unique更适合它自己的实际功能,因为它可以在许多其他地方使用。

可维护性:
在这种情况下,我会说 lambda 的可维护性很低,因为它们不能在函数外部重用,而且它们看起来很乱,因为它们挤满了它们所在的函数。所以最好在外面定义它们。

于 2013-11-12T03:29:53.037 回答
1

C++ 并不意味着支持惰性。使用 lambdas 代替普通函数在这里也不例外。

这个

int f(vector<int> v)
{
    sort(begin(v), end(v));
    v.erase(unique(begin(v), end(v)), end(v));
    copy(begin(v), end(v), ostream_iterator<int>(cout, " "));
    cout << endl;
    /* And then the function uses these helpers a few more times to justify making functions... */
}

比这更简洁、标准和可读

int f(vector<int> v)
{
    auto make_unique  = [](vector<int> &v)
    {
        sort(begin(v), end(v));
        auto unique_end = unique(begin(v), end(v));
        v.erase(unique_end, end(v));
    };
    auto print_vector = [](vector<int> const &v)
    {
        copy(begin(v), end(v), ostream_iterator<int>(cout, " "));
        cout << endl;
    };

   make_unique (v);
   print_vector(v);
   /* And then the function uses these helpers a few more times to justify making functions... */
}

作为一般的经验法则,当您的代码本身不晦涩(erase并且不晦涩sortcopy它们是标准的 C++ 算法)时,添加更多代码只会使情况变得更糟。

此外:没有理由在这么多地方限制vto的类型。 在类型无关紧要的地方使用类型推断。vector<int>

如果你觉得它应该是一个单独的函数,那么就让它完全成为一个单独的函数。
不要使用 lambdas 作为懒惰的拐杖。

于 2013-11-12T03:46:36.133 回答