All code details for this question are in this godbolt example:
这个问题的所有代码细节都在这个Godbolt示例中:
#include <array>
#include <iostream>
#include <random>
#include <range/v3/all.hpp>
#include <string>
#include <type_traits>
#include <unordered_set>
#include <vector>
#include "fmt/format.h"
#include "fmt/ranges.h"
template <typename T>
decltype(auto) deref(T&& t) {
return std::forward<T>(t).get();
}
template <ranges::range Range>
class deref_view : public ranges::view_base {
public:
struct iterator;
deref_view() = default;
deref_view(ranges::range auto&& base) : m_base(base) {}
iterator begin() { return ranges::begin(m_base); }
iterator end() { return ranges::end(m_base); }
private:
Range m_base;
};
template <ranges::range Range>
struct deref_view<Range>::iterator : public ranges::iterator_t<Range> {
using base = ranges::iterator_t<Range>;
using value_type =
std::remove_cvref_t<decltype(deref(*(std::declval<Range>().begin())))>;
using difference_type = ranges::range_difference_t<Range>;
iterator() = default;
iterator(const base& b) : base{b} {}
iterator operator++(int) { return static_cast<base&>(*this)++; }
iterator& operator++() {
++static_cast<base&>(*this);
return (*this);
}
decltype(auto) operator*() const {
return deref(*static_cast<base>(*this));
}
};
template <ranges::range Range>
deref_view(Range&&) -> deref_view<ranges::cpp20::views::all_t<Range> >;
struct deref_fn {
template <typename Rng>
auto operator()(Rng&& rng) const {
return deref_view{ranges::views::all(std::forward<Rng>(rng))};
}
template <typename Rng>
friend auto operator|(Rng&& rng, deref_fn const&) {
return deref_view{ranges::views::all(std::forward<Rng>(rng))};
}
};
// add the deref view to the ranges namespace
namespace ranges::views {
constexpr deref_fn deref{};
} // namespace ranges::views
int main() {
std::vector v{1, 2, 3, 4, 5, 6};
// auto list = {std::ref(v), std::ref(v)}; // this does NOT work
auto list = std::array{std::ref(v), std::ref(v)}; // this does NOT work
// auto list = std::vector{std::ref(v), std::ref(v)}; // this WORKS
for (const auto& arr : list | ranges::views::deref) {
fmt::print("{}\n", arr);
}
return 0;
}
Building on my previous question Create a custom transformable view in ranges-v3, I have a deref
view which merely dereferences std::reference_wrappers
by calling .get()
on the range's elements,
在我上一个问题的基础上,在Range-v3中创建一个定制的可转换视图,我有一个deref视图,它只是通过在范围的元素上调用.get()来取消引用std::Reference_wrappers,
template < typename T >
decltype(auto) deref(T&& t)
{
return std::forward< T >(t).get();
}
I have used this method to create a deref view for the ranges-v3 library which has worked for my use cases as expected. However, I cannot apply it to a std::array
or std::initializer_list
container, but it does work for a std::vector
. The error I get for e.g. std::array
is:
我已经使用此方法为Range-v3库创建了deref视图,该库已按预期用于我的用例。但是,我不能将其应用于std::数组或std::Initializer_list容器,但它确实适用于std::VECTOR。例如,对于std::数组,我得到的错误是:
<source>: In instantiation of 'struct deref_view<ranges::ref_view<std::array<std::reference_wrapper<std::vector<int> >, 2> > >::iterator':
<source>:120:49: required from here
<source>:66:29: error: base type 'ranges::iterator_t<ranges::ref_view<std::array<std::reference_wrapper<std::vector<int> >, 2> > >' {aka 'std::reference_wrapper<std::vector<int> >*'} fails to be a struct or class type
66 | struct deref_view< Range >::iterator: public ranges::iterator_t< Range > {
| ^~~~~~~~
<source>: In function 'int main()':
<source>:120:49: error: no match for 'operator!=' (operand types are 'deref_view<ranges::ref_view<std::array<std::reference_wrapper<std::vector<int> >, 2> > >::iterator' and 'deref_view<ranges::ref_view<std::array<std::reference_wrapper<std::vector<int> >, 2> > >::iterator')
120 | for(const auto& arr : list | ranges::views::deref) {
| ^~~~~
<source>: In instantiation of 'decltype(auto) deref_view<Range>::iterator::operator*() const [with Range = ranges::ref_view<std::array<std::reference_wrapper<std::vector<int> >, 2> >]':
<source>:120:49: required from here
<source>:83:53: error: invalid 'static_cast' from type 'const deref_view<ranges::ref_view<std::array<std::reference_wrapper<std::vector<int> >, 2> > >::iterator' to type 'deref_view<ranges::ref_view<std::array<std::reference_wrapper<std::vector<int> >, 2> > >::iterator::base' {aka 'std::reference_wrapper<std::vector<int> >*'}
83 | decltype(auto) operator*() const { return deref(*static_cast< base >(*this)); }
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
What specifics am I missing in how ranges handle arrays and vectors?
在范围处理数组和向量的方式中,我遗漏了哪些细节?
(The problem also occurs with std::ranges)
(std::Range也会出现此问题)
更多回答
优秀答案推荐
The compiler error here indicates the issue:
此处的编译器错误指示问题:
<source>:66:29: error: base type 'ranges::iterator_t<ranges::ref_view<std::array<std::reference_wrapper<std::vector<int> >, 2> > >' {aka 'std::reference_wrapper<std::vector<int> >*'} fails to be a struct or class type
When you write:
当你写下这封信时:
template <ranges::range Range>
struct deref_view<Range>::iterator : public ranges::iterator_t<Range> {
You're only allowed to inherit from class types. But iterator_t<Range>
need not actually be a class type. And in the particular case that you're trying here, it's not a class type - it's just a pointer (specifically std::reference_wrapper<std::vector<int> >*
). That's because std::array<T, N>
's iterator is allowed to be just T*
or T const*
(it's implementation defined, but a simple pointer meets all the requirements).
您只允许从类类型继承。但是Iterator_t
实际上不需要是类类型。在这里尝试的特定情况下,它不是一个类类型--它只是一个指针(具体地说,就是std::Reference_wrapper
*)。这是因为std::数组
‘S迭代器只允许为T*或T const*(它是实现定义的,但一个简单的指针满足所有要求)。
The solution here is straightforward: instead of inheriting from ranges::iterator_t<Base>
, have a member of that type. This is what all the range adapters have to do - first because of the pointer issue, since pointers are valid iterators, but also because avoiding inheritance ensures that you can actually get all the operations correct.
这里的解决方案很简单:不是从Range::Iterator_t继承,而是拥有该类型的成员。这是所有范围适配器都必须做的--首先是因为指针问题,因为指针是有效的迭代器,而且还因为避免继承确保您可以实际正确地执行所有操作。
This additionally actually makes the implementation cleaner anyway, since you can just refer to the base member instead of having to write static_cast<base>(*this)
everywhere.
此外,这实际上使实现更加简洁,因为您可以只引用基成员,而不必在任何地方编写Static_Cast(*this)。
Demo.
演示。
更多回答
thanks so much for your effort! I wasn't aware of this iterator oddity. It works well now with your adaptation :)
非常感谢您的努力!我没有意识到这种迭代器的奇怪之处。它现在可以很好地适应您的需求:)
我是一名优秀的程序员,十分优秀!