算法可以与表达式模板兼容吗
Can algorithms be made compatible with expression templates?
假设我有一些基于数组的代码,可以由表达式模板使用。例如,我使这些数组的operator[]
过载,还使算术运算符+
等过载。
现在我想让STL算法any_of
在这样的数组上运行。简单的方法是进行
ExprArray<double, N> b, c; // init etc.
auto a = b + c; // for (auto i = 0; i < N; ++i) { a[i] = b[i] + c[i]; }
auto res = std::any_of(begin(a), end(a), SomePred{});
当然,我希望能够缩短计算时间,并拥有一个改进的(基于范围的(lib::any_of
,它可以进行
// only compute b[i] + c[i] until SomePred is satisified
auto res = lib::any_of(b + c, SomePred{}); // write as explicit loop over b[i] + c[i]
根据operator[]
在其输入上写入lib::any_of
将完成该工作,与对过载的operator+
所做的相同。然而,这将需要对我可能在这样的数组上运行的所有STL算法进行类似的重新实现。
问题:所以假设我只想通过修改ExprArray iterators
来重用现有的基于范围的算法(Boost.range,range-v3(。是否可以修改ExprArray
迭代器operator*
和operator++
,使其对基于范围的算法透明?
// only compute b[i] + c[i] until SomePred is satisified
// needs to eventually dispatch to
// for (auto i = 0; i < N; ++i)
// if (SomePred(b[i] + c[i])) return true;
// return false;
auto res = ranges::any_of(b + c, SomePred{});
因此,如果算法版本实际上是用迭代器实现的,那么循环for (auto it = first; it != last; ++it)
需要*it
知道它需要计算b[i] + c[i]
,而++it
必须知道它需要执行++i
。
这个问题似乎简化为"我可以为我的表达式模板实现迭代器吗?"我认为这相当简单。假设"表达式模板"知道它们的size
并重载了operator[]
,迭代器只需要保存对表达式对象的引用和它所代表的范围的偏移:
template <class Expr>
class iterator {
public:
using iterator_category = ranges::random_access_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = typename Expr::value_type;
iterator() = default;
constexpr iterator(Expr& e, difference_type i) :
expr_{&e}, i_{i} {}
constexpr bool operator==(const iterator& that) const {
return assert(expr_ == that.expr_), i_ == that.i_;
}
constexpr bool operator!=(const iterator& that) const {
return !(*this == that);
}
// Similarly for operators <, >, <=, >=
value_type operator*() const {
return (*expr_)[i_];
}
value_type operator[](difference_type n) const {
return (*expr_)[i_ + n];
iterator& operator++() & { ++i_; }
iterator operator++(int) & { auto tmp = *this; ++*this; return tmp; }
// Similarly for operator--
iterator operator+(difference_type n) const {
return iterator{expr_, i_ + n};
}
// Similarly for operators -, +=, and -=
friend iterator operator+(difference_type n, const iterator& i) {
return i + n;
}
private:
Expr* expr_;
difference_type i_;
};
现在,您只需安排"表达式模板"具有返回iterator{*this, 0}
和iterator{*this, size()}
的begin
和end
成员。
这里的问题是b+c
返回什么。如果它返回一个真正的ExprArray
,那么就不可能真的有懒惰的评估。需要填充数组。不能存储对b
和c
的引用,因为您不知道它们的生存期。
然而,如果它返回一个LazyAddition
,其到ExprArray
的转换执行加法,那么LazyAddition::iterator
也可以实现延迟加法就很简单了。这里的风险是auto a = b+c
——这将创建一个具有挂起引用的LazyAddition
对象,而不是ExprArray
对象。
如果您试图在后台将ExprArray
实现为智能指针,情况会变得非常糟糕。当然,您可以实现写时复制,这样b+c
就是一个保持指向两个原始数组的指针的ExprArray。但是,一旦您调用T& ExprArray<T>::operator[]
,COW就会启动,并将整个数组复制到单个元素上读取!(const
上的C++运算符重载规则对operator[]
不起作用,当参数本身是const时会选择const版本,而不是用于读取访问时(
- 为什么这个运算符<重载函数对 STL 算法不可见?
- (C++)分析树以计算返回错误值的简单算术表达式
- 在VS2010-VS2015下编译时,如何使用decltype作为较大类型表达式的LHS
- 基于ELO的团队匹配算法
- 提升精神:解析布尔表达式并简化为规范范式
- C++选择排序算法中的逻辑错误
- 不能在初始值设定项列表中将非常量表达式从类型 'int' 缩小到'unsigned long long'
- 有没有办法将谓词中的元素偏移量传递给 std 算法?
- C++A*算法并不总是在路径中具有目标节点
- 排序算法c++
- 使用正则表达式regex_search在字符串中查找字符串
- 使用 llvm 和本地值编号算法擦除冗余表达式
- 错误:在"模板"之前预期主表达式,具有算法的复制功能
- 使用具有STL算法与Lambda表达式的函子
- 为什么C++标准使正则表达式算法成为免费函数
- 正则表达式算法
- 将算法c++的无效操作数替换为二进制表达式
- 正则表达式的算法 - 或上的组合
- 算法可以与表达式模板兼容吗
- c++标准算法,用最低优先级的运算符分隔一个表达式为两个表达式