C++函子优势-保持状态
C++ functor advantage - holding the state
我确实研究了函子的整个概念,不幸的是,我无法理解函子相对于典型函数的真正优势。
根据一些学术脚本,函子可以保持不同于函数的状态。有人能用一些简单易懂的例子来详细说明这一点吗?
我真的不明白为什么典型的正则函数不能做同样的事情。对于这种新手问题,我真的很抱歉。
作为一个非常琐碎的演示,让我们考虑一个快速排序。我们选择一个值(通常称为"pivot"),并将输入集合分为比pivot小的集合和比pivot1大的集合。
标准库已经有了std::partition
,它可以自己进行分区——将集合分为满足指定条件的项和不满足指定条件条件的项。因此,要进行分区,我们只需要提供一个合适的谓词。
在这种情况下,我们需要一个简单的比较,比如:return x < pivot;
。不过,每次传递枢轴值都会变得困难。std::partition
只是从集合中传递一个值,并询问:"这是否通过了您的测试?"您无法告诉std::partition
当前的枢轴值是什么,并让它在调用例程时将其传递给您的例程。当然,可以完成(例如,Windows中的许多枚举函数都是这样工作的),但它变得相当笨拙。
当我们调用std::partition
时,我们已经选择了枢轴值。我们想要的是一种方法…将该值绑定到将传递给比较函数的参数之一。一种非常丑陋的方法是通过一个全局变量"传递"它:
int pivot;
bool pred(int x) { return x < pivot; }
void quick_sort(int *begin, int *end) {
if (end - begin < 2)
return;
pivot = choose_pivot(begin, end);
int *pos = std::partition(begin, end, pred);
quick_sort(begin, pos);
quick_sort(pos, end);
}
我真的希望我不必指出,如果我们能帮助的话,我们宁愿不使用全局。避免这种情况的一个相当简单的方法是创建一个函数对象。当我们创建对象时,我们传递当前的pivot值,它将该值作为状态存储在对象中:
class pred {
int pivot;
public:
pred(int pivot) : pivot(pivot) {}
bool operator()(int x) { return x < pivot; }
};
void quick_sort(int *begin, int *end) {
if (end-begin < 2)
return;
int pivot = choose_pivot(begin, end);
int *pos = std::partition(begin, end, pred(pivot));
quick_sort(begin, pos);
quick_sort(pos, end);
}
这增加了一点点额外的代码,但作为交换,我们取消了一个全局交换——一个相当合理的交换。
当然,从C++11开始,我们还可以做得更好——该语言添加了"lambda表达式",可以为我们创建一个非常类似的类
void quick_sort(int *begin, int *end) {
if (end-begin < 2)
return;
int pivot = find_pivot(begin, end);
auto pos = std::partition(begin, end, [pivot](int x) { return x < pivot; });
quick_sort(begin, pos);
quick_sort(pos, end);
}
这改变了我们用来指定类/创建函数对象的语法,但它仍然与前面的代码基本相同:编译器生成一个带有构造函数和operator()
的类。我们用方括号括起来的值被传递给构造函数,(int x) { return x < pivot; }
基本上成为该类2的operator()
的主体。
这使得代码更容易编写,更容易阅读——但这并没有改变我们正在创建一个对象、"捕获"构造函数中的一些状态以及使用重载operator()
进行比较的基本事实。
当然,比较恰好是我们在排序之类的事情上所需要的。它是lambda表达式和函数对象的常见用法,但我们当然不限于此。举个例子,让我们考虑"规范化"一个doubles集合。我们想找到最大的一个,然后用它除以集合中的每个值,这样每个项目都在0.0到1.0的范围内,但所有项目都保持着与以前相同的比率:
double largest = * std::max_element(begin, end);
std::for_each(begin, end, [largest](double d) { return d/largest; });
在这里,我们有几乎相同的模式:创建一个存储一些相关状态的函数对象,然后重复应用该函数对象的operator()
来完成实际工作。
- 我们可以分为小于或等于和大于。或者我们可以创建三个组:小于、等于、大于。后者可以在存在许多重复的情况下提高效率,但目前我们真的不在乎
- 关于lambda表达式,我们需要了解的不仅仅是这个——我正在简化一些事情,而完全忽略其他我们目前不关心的事情
- Constexpr替代了新的放置方式,可以让内存中的对象保持未初始化状态
- 我不断收到 [错误] ID 返回 1 退出状态错误,但看不到问题所在
- OSX MetalKit CVMetalTextureCacheCreateTextureFromImage失败,状态:
- std::future_error:无关联状态
- 如何避免LED在循环状态变化中闪烁?
- boost 是否有按特殊类型值编码状态"compact optional"?
- 为什么系统函数总是在C++中返回已转移的退出状态?
- C++ 中的编译错误:未定义对"主"的引用 collect2:错误:ld 返回 1 个退出状态
- 当可输入框在窗口中处于活动状态时获得通知的任何方法
- 检查两个节点在子节点上是否具有相同状态的更优雅的方法
- 将有状态的 lambda 传递到 C 样式函数中,而无需上下文参数
- 在 nullptr 上调用无状态类的非静态成员函数是否合法?
- 编译问题:在函数"_start"中:未定义对"主"的引用 collect2:错误:ld 返回 1 个退出状态
- C++部分概念 id:显式模板规范顺序/第一个参数的特殊状态的原因是什么?
- 试图在崇高中奔跑. 错误 已发生: 收集2.exe: 错误: ld 返回 1 退出状态
- 在容器上移动分配:以前包含的对象的状态
- collect2:错误:ld 返回 1 个退出状态未定义的引用
- 具有动态大小的特征矩阵的默认初始状态
- 如何从Clojure调用C++程序,以使程序保持打开状态?
- q网络回复 无状态码或错误,但失败