我的自定义 std::ranges 迭代器缺少什么?
What am I missing in my custom std::ranges iterator?
我想为客户数据结构提供一个视图,它有自己的迭代器。我写了一个小程序来测试它,如下所示。如果我取消对 begin() 的注释,那么它就可以工作了。但是,如果我使用 DummyIter,则会出现编译错误。
在我的完整程序中,我实现了一个完整的迭代器,但为了简单起见,我在这里将其缩小到必要的功能。
#include <iostream>
#include <ranges>
#include <vector>
template<class T>
struct DummyIter
{
using iterator_category = std::random_access_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
DummyIter() = default;
auto operator*() const { T t; return t; }
auto& operator++() { return *this; }
auto operator++(int val) { DummyIter tmp = *this; ++*this; return tmp; }
auto operator==(const DummyIter& iter) const { return true; }
};
template<class V>
struct DummyView : std::ranges::view_interface<DummyView<V>>
{
//auto begin() const { return std::ranges::begin(v); }
auto begin() const { return DummyIter<int>(); }
auto end() const { return std::ranges::end(v); }
V v;
};
int main() {
auto view = DummyView<std::vector<int>>();
view | std::views::filter([](auto i) { return i > 0; });
}
我正在使用 GCC 11.1.0。我的迭代器中缺少什么才能使其符合范围?
error: no match for 'operator|' (operand types are 'DummyView<std::vector<int> >' and 'std::ranges::views::__adaptor::_Partial<std::ranges::views::_Filter, main()::<lambda(auto:15)> >')
37 | view | std::views::filter([](auto i) { return i > 0; });
| ~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | std::ranges::views::__adaptor::_Partial<std::ranges::views::_Filter, main()::<lambda(auto:15)> >
| DummyView<std::vector<int> >
这是行不通的,因为您的 begin
和 end
的 return 类型不匹配。
所以基本上这些迭代器不能相互比较。
证明:
- 这里是your code with extra static assert
- 这里是 dummy fix 编译,因为现在相同的 return 类型用于
begin
和 end
最低要求是 begin()
和 end()
的结果具有可比性。 begin()
和 end()
的不同类型在范围大小未知时很有用。这很好explanation of sentinel(在评论中提到)。
因为你的DummyView
不符合range
的概念,准确的说return类型的view.end()
不能[=29] =]作为view.begin()
的哨兵,因为DummyIter
没有合适的operator==()
来和std::vector<int>::const_iterator
比较,也就是说没有方式知道迭代器是否已经到达范围的末尾。
解决方法是将 bool operator==(std::vector<int>::const_iterator) const
添加到您的 DummyIter
template<class T>
struct DummyIter {
// ...
bool operator==(std::vector<T>::const_iterator) const;
};
检查这种东西的范围的方法是:
- 确认您的迭代器是
input_iterator
.
- 验证你的哨兵是
sentinel_for
你的迭代器。
这些检查会告诉您缺少什么功能。
在这种情况下,即:
using I = DummyIter<int>;
using S = std::vector<int>::const_iterator;
static_assert(std::input_iterator<I>); // ok
static_assert(std::sentinel_for<S, I>); // error
问题是 S
不是 sentinel_for<I>
,因为它不具有可比性。您需要知道迭代何时停止,并且您没有该运算符 - 您的 DummyIter<T>
与另一个 DummyIter<T>
相当,但与您 return 从 end()
.
因此您要么需要在 DummyIter<T>
中添加另一个 operator==
,要么 DummyView<V>::end()
return 某种与 [= 相当的 DummySentinel<V>
15=]。这取决于实际问题,哪种方法更好,标准库中都有这两种方法的示例。
我想为客户数据结构提供一个视图,它有自己的迭代器。我写了一个小程序来测试它,如下所示。如果我取消对 begin() 的注释,那么它就可以工作了。但是,如果我使用 DummyIter,则会出现编译错误。
在我的完整程序中,我实现了一个完整的迭代器,但为了简单起见,我在这里将其缩小到必要的功能。
#include <iostream>
#include <ranges>
#include <vector>
template<class T>
struct DummyIter
{
using iterator_category = std::random_access_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
DummyIter() = default;
auto operator*() const { T t; return t; }
auto& operator++() { return *this; }
auto operator++(int val) { DummyIter tmp = *this; ++*this; return tmp; }
auto operator==(const DummyIter& iter) const { return true; }
};
template<class V>
struct DummyView : std::ranges::view_interface<DummyView<V>>
{
//auto begin() const { return std::ranges::begin(v); }
auto begin() const { return DummyIter<int>(); }
auto end() const { return std::ranges::end(v); }
V v;
};
int main() {
auto view = DummyView<std::vector<int>>();
view | std::views::filter([](auto i) { return i > 0; });
}
我正在使用 GCC 11.1.0。我的迭代器中缺少什么才能使其符合范围?
error: no match for 'operator|' (operand types are 'DummyView<std::vector<int> >' and 'std::ranges::views::__adaptor::_Partial<std::ranges::views::_Filter, main()::<lambda(auto:15)> >')
37 | view | std::views::filter([](auto i) { return i > 0; });
| ~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | std::ranges::views::__adaptor::_Partial<std::ranges::views::_Filter, main()::<lambda(auto:15)> >
| DummyView<std::vector<int> >
这是行不通的,因为您的 begin
和 end
的 return 类型不匹配。
所以基本上这些迭代器不能相互比较。
证明:
- 这里是your code with extra static assert
- 这里是 dummy fix 编译,因为现在相同的 return 类型用于
begin
和end
最低要求是 begin()
和 end()
的结果具有可比性。 begin()
和 end()
的不同类型在范围大小未知时很有用。这很好explanation of sentinel(在评论中提到)。
因为你的DummyView
不符合range
的概念,准确的说return类型的view.end()
不能[=29] =]作为view.begin()
的哨兵,因为DummyIter
没有合适的operator==()
来和std::vector<int>::const_iterator
比较,也就是说没有方式知道迭代器是否已经到达范围的末尾。
解决方法是将 bool operator==(std::vector<int>::const_iterator) const
添加到您的 DummyIter
template<class T>
struct DummyIter {
// ...
bool operator==(std::vector<T>::const_iterator) const;
};
检查这种东西的范围的方法是:
- 确认您的迭代器是
input_iterator
. - 验证你的哨兵是
sentinel_for
你的迭代器。
这些检查会告诉您缺少什么功能。
在这种情况下,即:
using I = DummyIter<int>;
using S = std::vector<int>::const_iterator;
static_assert(std::input_iterator<I>); // ok
static_assert(std::sentinel_for<S, I>); // error
问题是 S
不是 sentinel_for<I>
,因为它不具有可比性。您需要知道迭代何时停止,并且您没有该运算符 - 您的 DummyIter<T>
与另一个 DummyIter<T>
相当,但与您 return 从 end()
.
因此您要么需要在 DummyIter<T>
中添加另一个 operator==
,要么 DummyView<V>::end()
return 某种与 [= 相当的 DummySentinel<V>
15=]。这取决于实际问题,哪种方法更好,标准库中都有这两种方法的示例。