我想知道是否可以以合理的方式在 C++ 中实现惰性求值。如果是的话,你会怎么做?
是的,这是可能的并且经常这样做,例如用于矩阵计算。促进这一点的主要机制是运算符重载。考虑矩阵加法的情况。函数的签名通常如下所示:
matrix operator +(matrix const& a, matrix const& b);
现在,为了使这个函数变得惰性,返回一个代理而不是实际结果就足够了:
struct matrix_add;
matrix_add operator +(matrix const& a, matrix const& b) {
return matrix_add(a, b);
}
现在需要做的就是编写这个代理:
struct matrix_add {
matrix_add(matrix const& a, matrix const& b) : a(a), b(b) { }
operator matrix() const {
matrix result;
// Do the addition.
return result;
}
private:
matrix const& a, b;
};
神奇之处在于方法operator matrix()
这是一个隐式转换运算符matrix_add
简单地matrix
。这样,您可以链接多个操作(当然通过提供适当的重载)。仅当最终结果分配给某个对象时,才会进行评估matrix
实例。
EDIT我应该说得更明确。事实上,该代码没有任何意义,因为尽管计算是延迟发生的,但它仍然发生在同一个表达式中。特别是,另一个添加将评估此代码,除非matrix_add
结构被更改为允许链式加法。 C++0x 通过允许可变模板(即可变长度的模板列表)极大地促进了这一点。
然而,在一个非常简单的情况下,这段代码实际上会带来真正、直接的好处,如下所示:
int value = (A + B)(2, 3);
这里,假设A
and B
是二维矩阵,解引用是用 Fortran 表示法完成的,即上面的计算one矩阵和中的元素。将整个矩阵相加当然是浪费的。matrix_add
救援:
struct matrix_add {
// … yadda, yadda, yadda …
int operator ()(unsigned int x, unsigned int y) {
// Calculate *just one* element:
return a(x, y) + b(x, y);
}
};
其他例子比比皆是。我刚刚想起来不久前我已经实现了一些相关的东西。基本上,我必须实现一个字符串类,该类应遵循固定的预定义接口。但是,我的特定字符串类处理实际未存储在内存中的巨大字符串。通常,用户只需使用函数访问原始字符串中的小子字符串infix
。我为我的字符串类型重载了这个函数,以返回一个代理,该代理保存对我的字符串的引用,以及所需的开始和结束位置。只有当这个子字符串被实际使用时,它才会查询 C API 来检索字符串的这一部分。