You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 16:49:54