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

如何在C++20中使用std::ranges::istream_view并执行后续操作?

解决std::ranges::istream_view结合std::views::split的编译错误

在网络及C++20相关书籍中,未找到任何展示std::ranges::istream_view结果结合后续操作的示例,尝试的每一项后续操作都会触发难以自行排查的编译错误。例如想要分割通过istream_view传入的惰性字符流,以下代码无法运行,但能表达需求:

#include <cstdlib>
#include <ranges>
#include <sstream>
#include <fmt/core.h>

auto main() -> int
{
  std::istringstream data{"a,b"};
  for (const auto& item:
      std::views::istream<std::string>(data)
    | std::views::split(",")
  ) {
    fmt::print("{}\n", item);
  }
  return EXIT_SUCCESS;
}

期望这段代码第一行输出"a",第二行输出"b",但GCC 12.2抛出如下编译错误:

无法执行程序
编译器返回值: 1
编译器标准错误输出:
<source>: 在函数'int main()'中:
<source>:11:5: 错误: 无法匹配'operator|'(操作数类型为'std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >'和'std::ranges::views::__adaptor::_Partial<std::ranges::views::_Split, const char*>')
   10 |       std::views::istream<std::string>(data)
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |                                       |
      |                                       std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >
   11 |     | std::views::split(",")
      |     ^ ~~~~~~~~~~~~~~~~~~~~~~
      |                        |
      |                        std::ranges::views::__adaptor::_Partial<std::ranges::views::_Split, const char*>
从<source>:2包含的文件:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:868:7: 注意: 候选: 'template<class _Lhs, class _Rhs>  requires (derived_from<_Lhs, std::ranges::views::__adaptor::_RangeAdaptorClosure>) && (derived_from<_Rhs, std::ranges::views::__adaptor::_RangeAdaptorClosure>) constexpr auto std::ranges::views::__adaptor::operator|(_Lhs, _Rhs)'
  868 |       operator|(_Lhs __lhs, _Rhs __rhs)
      |       ^~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:868:7: 注意:   模板参数推导/替换失败:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:868:7: 注意: 约束未满足
从/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:37包含的文件:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/concepts: 在替换'template<class _Lhs, class _Rhs>  requires (derived_from<_Lhs, std::ranges::views::__adaptor::_RangeAdaptorClosure>) && (derived_from<_Rhs, std::ranges::views::__adaptor::_RangeAdaptorClosure>) constexpr auto std::ranges::views::__adaptor::operator|(_Lhs, _Rhs) [with _Lhs = std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >; _Rhs = std::ranges::views::__adaptor::_Partial<std::ranges::views::_Split, const char*>]'时:
<source>:11:28:   要求从这里
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/concepts:67:13:   要求满足'derived_from<_Lhs, std::ranges::views::__adaptor::_RangeAdaptorClosure>' [with _Lhs = std::ranges::basic_istream_view<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char, std::char_traits<char> >]
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/concepts:67:28: 注意:   'std::ranges::views::__adaptor::_RangeAdaptorClosure'不是'std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >'的基类
   67 |     concept derived_from = __is_base_of(_Base, _Derived)
      |                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:859:7: 注意: 候选: 'template<class _Self, class _Range>  requires (derived_from<typename std::remove_cvref<_Tp>::type, std::ranges::views::__adaptor::_RangeAdaptorClosure>) && (__adaptor_invocable<_Self, _Range>) constexpr auto std::ranges::views::__adaptor::operator|(_Range&&, _Self&&)'
  859 |       operator|(_Range&& __r, _Self&& __self)
      |       ^~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:859:7: 注意:   模板参数推导/替换失败:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:859:7: 注意: 约束未满足
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges: 在替换'template<class _Self, class _Range>  requires (derived_from<typename std::remove_cvref<_Tp>::type, std::ranges::views::__adaptor::_RangeAdaptorClosure>) && (__adaptor_invocable<_Self, _Range>) constexpr auto std::ranges::views::__adaptor::operator|(_Range&&, _Self&&) [with _Self = std::ranges::views::__adaptor::_Partial<std::ranges::views::_Split, const char*>; _Range = std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >]'时:
<source>:11:28:   要求从这里
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:831:13:   要求满足'__adaptor_invocable<_Self, _Range>' [with _Self = std::ranges::views::__adaptor::_Partial<std::ranges::views::_Split, const char*>; _Range = std::ranges::basic_istream_view<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char, std::char_traits<char> >]
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:832:9:   在要求中 [with _Args = {std::ranges::basic_istream_view<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char, std::char_traits<char> >}; _Adaptor = std::ranges::views::__adaptor::_Partial<std::ranges::views::_Split, const char*>]
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:832:44: 注意: 所需表达式'declval<_Adaptor>()((declval<_Args>)()...)'无效
  832 |       = requires { std::declval<_Adaptor>()(declval<_Args>()...); };
      |                    ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
cc1plus: 注意: 设置'-fconcepts-diagnostics-depth='至少为2以获取更多细节
从/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/streambuf:41包含的文件:
                 从/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/streambuf_iterator.h:35,
                 从/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/iterator:66,
                 从/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:43:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/ios_base.h:87:3: 注意: 候选: 'constexpr std::_Ios_Fmtflags std::operator|(_Ios_Fmtflags, _Ios_Fmtflags)'
   87 |   operator|(_Ios_Fmtflags __a, _Ios_Fmtflags __b)
      |   ^~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/ios_base.h:87:27: 注意:   无法将参数1从'std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >'转换为'std::_Ios_Fmtflags'
   87 |   operator|(_Ios_Fmtflags __a, _Ios_Fmtflags __b)
      |             ~~~~~~~~~~~~~~^~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/ios_base.h:130:3: 注意: 候选: 'constexpr std::_Ios_Openmode std::operator|(_Ios_Openmode, _Ios_Openmode)'
  130 |   operator|(_Ios_Openmode __a, _Ios_Openmode __b)
      |   ^~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/ios_base.h:130:27: 注意:   无法将参数1从'std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >'转换为'std::_Ios_Openmode'
  130 |   operator|(_Ios_Openmode __a, _Ios_Openmode __b)
      |             ~~~~~~~~~~~~~~^~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/ios_base.h:170:3: 注意: 候选: 'constexpr std::_Ios_Iostate std::operator|(_Ios_Iostate, _Ios_Iostate)'
  170 |   operator|(_Ios_Iostate __a, _Ios_Iostate __b)
      |   ^~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/ios_base.h:170:26: 注意:   无法将参数1从'std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >'转换为'std::_Ios_Iostate'
  170 |   operator|(_Ios_Iostate __a, _Ios_Iostate __b)
      |             ~~~~~~~~~~~~~^~~
从/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/span:42包含的文件:
                 从/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:45:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/cstddef:132:3: 注意: 候选: 'constexpr std::byte std::operator|(byte, byte)'
  132 |   operator|(byte __l, byte __r) noexcept
      |   ^~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/cstddef:132:18: 注意:   无法将参数1从'std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >'转换为'std::byte'
  132 |   operator|(byte __l, byte __r) noexcept
      |             ~~~~~^~~

问题根源与修正方案

原代码存在两个核心问题:

  1. std::views::istream<std::string>的行为不符合预期:该视图会按空白字符分割读取流中的字符串,因此会一次性读取整个"a,b",而不是逐个读取字符,导致后续的split操作无法按逗号分割单个字符流。
  2. split的使用方式不匹配:std::views::split(",")接受的是字符串字面量,但原视图的元素是std::string,split会尝试将每个字符串元素按逗号分割,同时split返回的子range无法直接被fmt格式化输出。

修正后的代码如下:

#include <cstdlib>
#include <ranges>
#include <sstream>
#include <fmt/core.h>
#include <string>

auto main() -> int
{
  std::istringstream data{"a,b"};
  for (const auto& subrange :
       std::views::istream<char>(data)
       | std::views::split(',')
  ) {
    fmt::print("{}\n", std::string(subrange.begin(), subrange.end()));
  }
  return EXIT_SUCCESS;
}

修正说明

  • 将std::views::istream<std::string>改为std::views::istream<char>,实现逐个读取流中的字符,确保split操作能处理完整的字符流。
  • 分隔符从字符串字面量","改为字符',',匹配当前视图的元素类型(char)。
  • 遍历split返回的子range时,将其转换为std::string后再打印,因为split返回的是子range对象,无法直接被fmt格式化。

内容的提问来源于stack exchange,提问作者Wouter Beek

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 14:45:48