提问人:user904963 提问时间:11/12/2013 最后编辑:dypuser904963 更新时间:1/10/2023 访问量:2613
使用 lambda 函数来定义非常小的辅助函数是好的风格吗?
Is it good style to use lambda functions to define very small helper functions?
问:
举个愚蠢的例子,假设我有一个函数,出于某种原因,我需要对 中的几个操作进行几次操作。与其将辅助函数放在其他地方(这可能会增加混乱并损害可读性),不如这样做有什么优点和缺点(效率、可读性、可维护性等):int f(vector<int> v)
v
f
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...
}
还是有一些首选的替代方案?
答:
效率:
这基本上是函数与函子,这就是 lambda 在引擎盖下的内容。函子实际上可以更快,这是因为它们更容易内联,并非所有编译器都不会内联函数指针(尽管这是可能的)。这样做的原因是,当您传递函数指针时,编译器只知道函数的类型,而函子具有整个函数,因为类型是唯一的。
可读性:
这部分或多或少是基于意见的。函数指针 IMO 非常烦人,它们有丑陋的类型,并且不像函子那样通用。例如,函子可以很容易地在运行时传递。另一方面,写出一个完整的函数比一个大的 lambda 更具可读性。
我想说的最后一点是,像函子一样的 lambda 可以有一个状态(不像函数,除非你计算静态变量),使用 lambda 你可以捕获一个变量。由于编译器内联的限制,最好不要传递函数指针。
所以我想说,对于传递给 stdlib 的小函数,最好使用 lambdas,而对于实现函子的大函数,函数指针在 c++11 中真的不再是惯用的。
不幸的是,在我看来,您使用它们的方式并不好。编译器将它们作为普通函数内联是没有问题的。一个名为 or 的函数更适合它自己的实际功能,因为它可以在许多其他地方使用。print_vector
make_unique
可维护性:
在这种情况下,我会说 lambda 的可维护性很低,因为它们不能在函数之外重用,而且它们看起来很混乱,因为它们挤满了它们所在的函数。因此,最好在外面定义它们。
评论
functor has the whole function
int(*)(const int &)
C++ 并不意味着支持懒惰。在这里使用 lambda 而不是普通函数也不例外。
这
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... */
}
作为一般的经验法则,当你的代码本身并不晦涩难懂时(并且不是晦涩难懂的,它们是标准的 C++ 算法),添加更多的代码只会让情况变得更糟。erase
sort
copy
此外:没有理由在这么多地方限制 to 的类型。
在类型无关紧要的地方使用类型推断。v
vector<int>
如果你觉得它应该是一个单独的函数,那么就让它完全成为一个单独的函数。
不要把lambdas当作懒惰的拐杖。
评论
make_unique
v
/* uniquify */ { ... }
这种本地范围函数的优点是,它们不会用“帮助程序”定义污染周围的代码,所有行为都可以限制在单个作用域内。而且,由于它们可以访问周围函数的词法范围,因此它们可用于分解行为,而无需传递许多参数。
您还可以使用它们来创建小型 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;
缺点是,您可能不必要地隐藏和专门化可能对其他定义有用的泛型函数。A 或函数值得浮出并重用。uniquify
print_vector
评论
make_unique