C++20自定义拼接视图无法与std::views::transform组合问题
自定义拼接视图ConcatenatedView与transform链式调用报错问题
我实现了一个用于拼接多个范围的自定义视图ConcatenatedView,单独使用时功能正常,能够拼接不同类型的范围并将结果存入vector。但将其与std::views::transform等视图链式调用时,出现如下编译错误:
error: no type named 'iterator_category' in 'struct std::iterator_traits<std::ranges::transform_view<ConcatenatedView<const std::vector<int, std::allocator
&>, const std::ranges::iota_view<int, int>&>, Concatenate_DifferentTypes_Test::TestBody()::<lambda(auto:43)> >::_Iterator >'
ConcatenatedView定义
namespace detail { template <typename Arg, typename... Args> struct GetFirstArgStruct { using Type = Arg; }; template <typename... Args> using GetFirstArg = GetFirstArgStruct<Args...>::Type; // Can't be declared in the ConcatenatedView struct as explicit specialization must be in namespace scope. template <typename... Views> struct ConcatenatedIterator; template <typename View, typename... RemainingViews> struct ConcatenatedIterator<View, RemainingViews...> { // Required for ranges, means we can't use references, only pointers. ConcatenatedIterator() {} ConcatenatedIterator(View view, RemainingViews... rest) : view_{view}, iter_{view_.begin()}, rest_(rest...) {} bool IsEnd() const { if (iter_ == view_.end()) return rest_.IsEnd(); return false; } // This returns a copy. We could use decltype(auto), but would need to ensure that all views return either // values or references. auto operator*() const { if constexpr (sizeof...(RemainingViews) != 0) { if (iter_ == view_.end()) return *rest_; } return *iter_; } ConcatenatedIterator& operator++() { if (iter_ == view_.end()) { if constexpr (sizeof...(RemainingViews) != 0) ++rest_; } else { ++iter_; } return *this; } private: friend bool operator==(const ConcatenatedIterator&, const ConcatenatedIterator&) = default; View view_; std::ranges::iterator_t<View> iter_; ConcatenatedIterator<RemainingViews...> rest_; }; template <> struct ConcatenatedIterator<> { bool IsEnd() const { return true; } }; } // namespace detail template <std::ranges::range... Ranges> struct ConcatenatedView : public std::ranges::view_interface<ConcatenatedView<Ranges...>> { public: struct Sentinel {}; template <std::ranges::view... Views> struct Iterator { using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = std::ranges::range_value_t<detail::GetFirstArg<Views...>>; using pointer = value_type*; using reference = value_type&; Iterator() {} // Requirement for ranges. Iterator(Views... views) : iter_(views...) {} decltype(auto) operator*() const { return *iter_; } Iterator& operator++() { ++iter_; return *this; } Iterator operator++(int) { Iterator tmp = *this; ++(*this); return tmp; } // TODO: Support sized view when all the ranges are sized. private: friend bool operator==(const Iterator& it, Sentinel) { return it.iter_.IsEnd(); } friend bool operator==(const Iterator&, const Iterator&) = default; detail::ConcatenatedIterator<Views...> iter_; }; using IterT = Iterator<decltype(std::ranges::views::all(std::forward<Ranges>(std::declval<Ranges&&>())))...>; ConcatenatedView() {} // Required for a view. ConcatenatedView(Ranges&&... ranges) : views_(std::ranges::views::all(std::forward<Ranges>(ranges))...) {} IterT begin() const { return std::make_from_tuple<IterT>(views_); } Sentinel end() const { return {}; }; private: std::tuple<decltype(std::ranges::views::all(std::forward<Ranges>(std::declval<Ranges&&>())))...> views_; }; template <std::ranges::range... Ranges> ConcatenatedView<Ranges...> Concatenate(Ranges&&... ranges) { return {std::forward<Ranges>(ranges)...}; }
测试代码
const auto vector = std::vector{1, 2, 3}; const auto iota = std::views::iota(4, 7); const auto concat = Concatenate(vector, iota) | std::views::common | std::views::transform([](const auto i) { return i + 1; }); auto result = std::vector<std::ranges::range_value_t<decltype(concat)>>{}; result.insert(result.end(), concat.begin(), concat.end());
尝试过的解决方法
- 在自定义视图与
transform视图之间添加std::views::common,但问题未解决。 - 根据
std::views::transform文档说明,若Base不满足forward_range则不会定义iterator_category,但通过静态断言确认视图符合forward_range要求:
static_assert( std::derived_from<std::iterator_traits<decltype(c.begin())>::iterator_category, std::forward_iterator_tag> == true);
内容的提问来源于stack exchange,提问作者Matt Oslin
相关产品推荐
相关产品推荐

