range-v3:调整已实现迭代器接口的自定义 类 (begin/end)
range-v3: Adapting custom classes which already implement iterator interfaces (begin/end)
我有一个实现 begin
和 end
的自定义容器。如何将此容器通过管道传输到 ranges-v3 视图?
std::vector 是可管道化的,所以我尝试以相同的方式管道化我的自定义 class,但是找不到我的容器的管道运算符。
我查看了文档,但除了重新实现带有 Range 接口的包装器 class 之外,我找不到任何其他方法。我有多个这样的 classes,我相信这可能是一个相当常见的情况,所以我宁愿使用图书馆提供的一些功能(或 class 基础),但我无法理解来自文档。
这是一个最小的例子:
#include <iostream>
#include <iterator>
#include <range/v3/all.hpp>
struct Test {
struct iterator;
struct sentinel {};
int counter;
Test() = default;
iterator begin();
sentinel end() const { return {}; }
iterator begin() const;
};
struct Test::iterator {
using value_type = int;
using reference = int&;
using pointer = int*;
using iterator_category = std::input_iterator_tag;
using difference_type = void;
Test* test;
iterator& operator++() {
test->counter++;
return *this;
}
iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
int operator*() { return test->counter; }
int operator*() const { return test->counter; }
bool operator!=(const iterator& rhs) const {
return rhs.test != test;
}
bool operator!=(sentinel) const {
return true;
}
};
Test::iterator Test::begin() { return iterator {this}; }
Test::iterator Test::begin() const { return iterator {const_cast<Test*>(this)}; }
int main() {
auto container = Test();
static_assert(ranges::range<Test>, "It is not a range");
static_assert(ranges::viewable_range<Test>, "It is not a viewable range");
auto rng = container | ranges::views::take(10);
for (auto n : rng) { std::cerr << n << std::endl;}
return 0;
}
这是我在使用这段代码时遇到的错误:
~/tmp/range$ g++ main.cpp -Irange-v3/include -o main 2>&1 | grep error
main.cpp:46:19: error: static assertion failed: It is not a range
main.cpp:47:19: error: static assertion failed: It is not a viewable range
range-v3/include/range/v3/functional/pipeable.hpp:63:53: error: no matching function for call to ‘ranges::pipeable_access::impl<ranges::views::view<ranges::make_pipeable_fn::operator()(Fun) const [with Fun = ranges::detail::bind_back_fn_<ranges::views::take_fn, int>]::_> >::pipe(Test&, ranges::views::view<ranges::make_pipeable_fn::operator()(Fun) const [with Fun = ranges::detail::bind_back_fn_<ranges::views::take_fn, int>]::_>&)’
main.cpp:48:10: error: ‘void rng’ has incomplete type
main.cpp:49:19: error: unable to deduce ‘auto&&’ from ‘rng’
这是我的解决方案:
template <typename T>
class ContainerView : public ranges::view_facade<ContainerView<T>> {
friend ranges::range_access;
using iterator_type = decltype(std::declval<T>().begin());
using value_type = decltype(*std::declval<iterator_type>());
T* container_;
iterator_type it_;
value_type read() const { return *it_; }
bool equal(ranges::default_sentinel_t) const {
return !(it_ != container_->end());
}
void next() { ++it_; }
public:
ContainerView() = default;
ContainerView(T& container)
: container_(&container), it_(container_->begin()) {}
using ranges::view_facade<ContainerView<T>>::begin;
using ranges::view_facade<ContainerView<T>>::end;
};
int main() {
auto container = Test();
auto view = ContainerView(container);
static_assert(ranges::range<decltype(view)>, "It is not a range");
static_assert(ranges::viewable_range<decltype(view)>,
"It is not a viewable range");
auto rng = view | ranges::views::take(10);
for (auto n : rng) {
std::cerr << n << std::endl;
}
return 0;
}
我不确定我是否重新实现了库中已经实现的东西。所以,我暂时不会将其标记为答案。如果有这样做的现有方法,请发表评论或 post 一个新答案。
- 为了建模
sentinel_for
,迭代器和哨兵必须在两个方向上使用 ==
和 !=
进行比较。 (在新世界中输入迭代器没有必要相互比较。)你只提供了 !=
并且只在一个方向上。
对于输入迭代器,difference_type
不能是 void
。它必须是有符号整数类型,例如 ptrdiff_t
.
另外:
reference
应该是operator*
的return类型;它不必是引用类型。
viewable_range<Test>
询问右值 Test
是否可见。由于您正在尝试查看左值,请考虑使用 Test&
.
我有一个实现 begin
和 end
的自定义容器。如何将此容器通过管道传输到 ranges-v3 视图?
std::vector 是可管道化的,所以我尝试以相同的方式管道化我的自定义 class,但是找不到我的容器的管道运算符。
我查看了文档,但除了重新实现带有 Range 接口的包装器 class 之外,我找不到任何其他方法。我有多个这样的 classes,我相信这可能是一个相当常见的情况,所以我宁愿使用图书馆提供的一些功能(或 class 基础),但我无法理解来自文档。
这是一个最小的例子:
#include <iostream>
#include <iterator>
#include <range/v3/all.hpp>
struct Test {
struct iterator;
struct sentinel {};
int counter;
Test() = default;
iterator begin();
sentinel end() const { return {}; }
iterator begin() const;
};
struct Test::iterator {
using value_type = int;
using reference = int&;
using pointer = int*;
using iterator_category = std::input_iterator_tag;
using difference_type = void;
Test* test;
iterator& operator++() {
test->counter++;
return *this;
}
iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
int operator*() { return test->counter; }
int operator*() const { return test->counter; }
bool operator!=(const iterator& rhs) const {
return rhs.test != test;
}
bool operator!=(sentinel) const {
return true;
}
};
Test::iterator Test::begin() { return iterator {this}; }
Test::iterator Test::begin() const { return iterator {const_cast<Test*>(this)}; }
int main() {
auto container = Test();
static_assert(ranges::range<Test>, "It is not a range");
static_assert(ranges::viewable_range<Test>, "It is not a viewable range");
auto rng = container | ranges::views::take(10);
for (auto n : rng) { std::cerr << n << std::endl;}
return 0;
}
这是我在使用这段代码时遇到的错误:
~/tmp/range$ g++ main.cpp -Irange-v3/include -o main 2>&1 | grep error
main.cpp:46:19: error: static assertion failed: It is not a range
main.cpp:47:19: error: static assertion failed: It is not a viewable range
range-v3/include/range/v3/functional/pipeable.hpp:63:53: error: no matching function for call to ‘ranges::pipeable_access::impl<ranges::views::view<ranges::make_pipeable_fn::operator()(Fun) const [with Fun = ranges::detail::bind_back_fn_<ranges::views::take_fn, int>]::_> >::pipe(Test&, ranges::views::view<ranges::make_pipeable_fn::operator()(Fun) const [with Fun = ranges::detail::bind_back_fn_<ranges::views::take_fn, int>]::_>&)’
main.cpp:48:10: error: ‘void rng’ has incomplete type
main.cpp:49:19: error: unable to deduce ‘auto&&’ from ‘rng’
这是我的解决方案:
template <typename T>
class ContainerView : public ranges::view_facade<ContainerView<T>> {
friend ranges::range_access;
using iterator_type = decltype(std::declval<T>().begin());
using value_type = decltype(*std::declval<iterator_type>());
T* container_;
iterator_type it_;
value_type read() const { return *it_; }
bool equal(ranges::default_sentinel_t) const {
return !(it_ != container_->end());
}
void next() { ++it_; }
public:
ContainerView() = default;
ContainerView(T& container)
: container_(&container), it_(container_->begin()) {}
using ranges::view_facade<ContainerView<T>>::begin;
using ranges::view_facade<ContainerView<T>>::end;
};
int main() {
auto container = Test();
auto view = ContainerView(container);
static_assert(ranges::range<decltype(view)>, "It is not a range");
static_assert(ranges::viewable_range<decltype(view)>,
"It is not a viewable range");
auto rng = view | ranges::views::take(10);
for (auto n : rng) {
std::cerr << n << std::endl;
}
return 0;
}
我不确定我是否重新实现了库中已经实现的东西。所以,我暂时不会将其标记为答案。如果有这样做的现有方法,请发表评论或 post 一个新答案。
- 为了建模
sentinel_for
,迭代器和哨兵必须在两个方向上使用==
和!=
进行比较。 (在新世界中输入迭代器没有必要相互比较。)你只提供了!=
并且只在一个方向上。
对于输入迭代器, difference_type
不能是void
。它必须是有符号整数类型,例如ptrdiff_t
.
另外:
reference
应该是operator*
的return类型;它不必是引用类型。viewable_range<Test>
询问右值Test
是否可见。由于您正在尝试查看左值,请考虑使用Test&
.