考虑这个示例(使用cppitertools):
#include <vector>
#include <iostream>
#include <cppitertools/enumerate.hpp>
#include <cppitertools/filter.hpp>
int
main()
{
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8};
auto f = iter::enumerate(v) | iter::filter([](auto& i) { return std::get<1>(i) > 4; });
for (auto&& [i, e]: f) {
std::cout << i << ", " << e << '\n';
}
}我要做的是过滤来自iter::enumerate的结果(只尝试获取属于> 4的值)。不幸的是,这个片段没有编译(g++ 7.4):
错误:
error: use of deleted function ‘std::optional<iter::impl::EnumIterYield<long unsigned int, int&> >& std::optional<iter::impl::EnumIterYield<long unsigned int, int&> >::operator=(std::optional<iter::impl::EnumIterYield<long unsigned int, int&> >&&)’
...
error: use of deleted function ‘std::_Enable_copy_move<true, false, true, false, _Tag>& std::_Enable_copy_move<true, false, true, false, _Tag>::operator=(std::_Enable_copy_move<true, false, true, false, _Tag>&&) [with _Tag = std::optional<iter::impl::EnumIterYield<long unsigned int, int&> >]’预期结果:
4, 5
5, 6
6, 7
7, 8当我移除iter::enumerate(v)并且只对普通向量进行过滤时,它可以工作:
auto f = v | iter::filter([](auto& i) { return i > 4; });
for (auto&& i: f) {
std::cout << i << '\n';
}指纹:
5
6
7
8我能过滤iter::enumerate()的结果吗?我怀疑我把iter::filter中的lambda函数搞错了。
发布于 2019-08-25 20:44:44
iter::enumerate返回一个iter::impl::Enumerable类型的实例。它的迭代器(一旦取消引用)返回iter::impl::EnumIterYield<unsigned long, int&>,它是从std::pair<unsigned long, int&>派生的类类型,但也重新声明自己的数据成员,其index (first)数据成员存储值索引,而element (second)是对容器的实际值的引用(参见int&)。这很好,因为我们希望能够通过迭代器修改容器的内容,并避免不必要的副本:
std::vector<int> v{1, 2, 3};
auto e = iter::enumerate(v);
std::get<1>(*e.begin()) = 5;实现该类的方式如下:
template <typename Index, typename Elem>
using EnumBasePair = std::pair<Index, Elem>;
template <typename Index, typename Elem>
class EnumIterYield : public EnumBasePair<Index, Elem> {
using BasePair = EnumBasePair<Index, Elem>;
using BasePair::BasePair;
public:
typename BasePair::first_type index = BasePair::first;
typename BasePair::second_type element = BasePair::second;
};但是,iter::impl::EnumIterYield类型不声明它自己的复制/移动赋值操作符,这使得该类的对象不可复制-可分配(这稍后将很重要)-因此,如果index或element是引用类型(或const),则隐式声明的复制赋值运算符将被定义为删除。
更进一步,通过使用iter::enumerate过滤iter::filter的结果,将创建另一个对象,这次是iter::impl::Filtered类型。它的迭代器存储一个名为DerefHolder的_item。DerefHolder应该保存在包装迭代器的取消引用的值中:
// DerefHolder holds the value gotten from an iterator dereference
// if the iterate dereferences to an lvalue references, a pointer to the
// element is stored
// if it does not, a value is stored instead也就是说,当模板参数是引用类型时,DerefHolder是专门用于这种情况的,在这种情况下,它存储一个(复制可分配的)指针。对于非引用类型,实际值存储在std::optional (称为item_p_)中。
对于引用类型具有DerefHolder的专门化,可以直接使用iter::impl::Filtered::Iterator对筛选的容器值进行操作。
但是,取消引用的iter::impl::Enumerable::Iterator不会导致引用类型。如前所述,它是一个不可复制的iter::impl::EnumIterYield<unsigned long, int&>,它不触发专门化,而是使编译器返回到DerefHolder的主模板。它的reset()函数在找到第一个不应该过滤的值时调用,定义如下:
void reset(T&& item) {
item_p_ = std::move(item);
}也就是说,它调用std::optional<iter::impl::EnumIterYield<unsigned long, int&>>::operator=,而后者又试图复制-分配有问题的EnumIterYield (在没有显式定义的复制/移动赋值操作符的情况下保存引用类型成员),但没有这样做。这就是错误消息报告的内容:
error: use of deleted function我认为没有理由尝试复制分配存储的值,因为它同样可以重新创建,因此我建议将实现改为复制(移动)-construct存储在std::optional中的项。
void reset(T&& item) {
item_p_.emplace(std::move(item));
}https://stackoverflow.com/questions/57648457
复制相似问题