使用标准范围将一系列字符串与定界符连接起来

Joining a range of strings with a delimiter using standard ranges

我想使用范围将跨度中包含的四个字节转换为字符串。这是输入和输出的示例:

std::span<std::byte> data{bytes}; // contains 0x11, 0x22, 0x33, 0x44

std::string result = ...; // results in 44:33:22:11

这是我根据 range-v3 how to action::join with delimiter 上的答案得出的结论:

auto const result = data
    | views::reverse
    | views::transform([](std::byte byte) { return fmt::format("{:02x}", byte); })
    | views::join(':');

但是,它无法编译,非常...让我们说说详细的错误:

 file.cpp: In function 'my_function {anonymous}::decode_function(std::span<const std::byte>)':
 file.cpp:87:21: error: no match for call to '(const std::ranges::views::__adaptor::_RangeAdaptorClosure<std::ranges::views::<lambda(_Range&&)> >) (char)'
    87 |    | views::join(':');
       |                     ^
 In file included from file.cpp:3:
 /usr/include/c++/10/ranges:1157:4: note: candidate: 'constexpr auto std::ranges::views::__adaptor::_RangeAdaptorClosure<_Callable>::operator()(_Range&&) const [with _Range = char; _Callable = std::ranges::views::<lambda(_Range&&)>]'
  1157 |    operator()(_Range&& __r) const
       |    ^~~~~~~~
 /usr/include/c++/10/ranges:1157:4: note: constraints not satisfied
 In file included from /usr/include/c++/10/string_view:44,
                  from file.h:4,
                  from file.cpp:1:
 /usr/include/c++/10/bits/range_access.h: In instantiation of 'constexpr auto std::ranges::views::__adaptor::_RangeAdaptorClosure<_Callable>::operator()(_Range&&) const [with _Range = char; _Callable = std::ranges::views::<lambda(_Range&&)>]':
 file.cpp:87:21:   required from here
 /usr/include/c++/10/bits/range_access.h:861:13:   required for the satisfaction of 'range<_Tp>' [with _Tp = char]
 /usr/include/c++/10/ranges:78:13:   required for the satisfaction of 'viewable_range<_Range>' [with _Range = char]
 /usr/include/c++/10/bits/range_access.h:861:21:   in requirements with 'char& __t'
 /usr/include/c++/10/bits/range_access.h:863:15: note: the required expression 'std::ranges::__cust::begin(__t)' is invalid, because
   863 |  ranges::begin(__t);
       |  ~~~~~~~~~~~~~^~~~~
 /usr/include/c++/10/bits/range_access.h:863:15: error: no match for call to '(const std::ranges::__cust_access::_Begin) (char&)'
 /usr/include/c++/10/bits/range_access.h:399:2: note: candidate: 'constexpr auto std::ranges::__cust_access::_Begin::operator()(_Tp&&) const [with _Tp = char&]'
   399 |  operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp>())
       |  ^~~~~~~~
 /usr/include/c++/10/bits/range_access.h:399:2: note: constraints not satisfied
 /usr/include/c++/10/bits/range_access.h: In instantiation of 'constexpr auto std::ranges::__cust_access::_Begin::operator()(_Tp&&) const [with _Tp = char&]':
 /usr/include/c++/10/bits/range_access.h:863:15:   required from 'constexpr auto std::ranges::views::__adaptor::_RangeAdaptorClosure<_Callable>::operator()(_Range&&) const [with _Range = char; _Callable = std::ranges::views::<lambda(_Range&&)>]'
 file.cpp:87:21:   required from here
 /usr/include/c++/10/bits/range_access.h:399:2:   required by the constraints of 'template<class _Tp>  requires (__maybe_borrowed_range<_Tp>) && ((is_array_v<typename std::remove_reference<_Tp>::type>) || (__member_begin<_Tp>) || (__adl_begin<_Tp>)) constexpr auto std::ranges::__cust_access::_Begin::operator()(_Tp&&) const'
 /usr/include/c++/10/bits/range_access.h:397:4: note: no operand of the disjunction is satisfied
   396 |  requires is_array_v<remove_reference_t<_Tp>> || __member_begin<_Tp>
       |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   397 |    || __adl_begin<_Tp>
       |    ^~~~~~~~~~~~~~~~~~~
 /usr/include/c++/10/bits/range_access.h:396:11: note: the operand 'is_array_v<std::remove_reference_t<_Tp> >' is unsatisfied because
   396 |  requires is_array_v<remove_reference_t<_Tp>> || __member_begin<_Tp>
       |           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   397 |    || __adl_begin<_Tp>
       |    ~~~~~~~~~~~~~~~~~~~
 /usr/include/c++/10/bits/range_access.h:399:2:   required by the constraints of 'template<class _Tp>  requires (__maybe_borrowed_range<_Tp>) && ((is_array_v<typename std::remove_reference<_Tp>::type>) || (__member_begin<_Tp>) || (__adl_begin<_Tp>)) constexpr auto std::ranges::__cust_access::_Begin::operator()(_Tp&&) const'
 /usr/include/c++/10/bits/range_access.h:396:11: note: the expression 'is_array_v<typename std::remove_reference<_Tp>::type> [with _Tp = char&; _Tp = char&]' evaluated to 'false'
   396 |  requires is_array_v<remove_reference_t<_Tp>> || __member_begin<_Tp>
       |           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 /usr/include/c++/10/bits/range_access.h:396:50: note: the operand '__member_begin<_Tp>' is unsatisfied because
   396 |  requires is_array_v<remove_reference_t<_Tp>> || __member_begin<_Tp>
       |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
   397 |    || __adl_begin<_Tp>
       |    ~~~~~~~~~~~~~~~~~~~                            
 In file included from /usr/include/c++/10/bits/stl_iterator_base_types.h:71,
                  from /usr/include/c++/10/bits/stl_algobase.h:65,
                  from /usr/include/c++/10/bits/char_traits.h:39,
                  from /usr/include/c++/10/string_view:41,
                  from file.h:4,
                  from file.cpp:1:
 /usr/include/c++/10/bits/iterator_concepts.h:847:15:   required for the satisfaction of '__member_begin<_Tp>' [with _Tp = char&]
 /usr/include/c++/10/bits/iterator_concepts.h:847:32:   in requirements with 'char& __t'
 /usr/include/c++/10/bits/iterator_concepts.h:849:28: note: the required expression 'std::__detail::__decay_copy(__t.begin())' is invalid, because
   849 |    { __detail::__decay_copy(__t.begin()) } -> input_or_output_iterator;
       |      ~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
 /usr/include/c++/10/bits/iterator_concepts.h:849:33: error: request for member 'begin' in '__t', which is of non-class type 'char'
   849 |    { __detail::__decay_copy(__t.begin()) } -> input_or_output_iterator;
       |                             ~~~~^~~~~
 In file included from /usr/include/c++/10/string_view:44,
                  from file.h:4,
                  from file.cpp:1:
 /usr/include/c++/10/bits/range_access.h:397:7: note: the operand '__adl_begin<_Tp>' is unsatisfied because
   396 |  requires is_array_v<remove_reference_t<_Tp>> || __member_begin<_Tp>
       |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   397 |    || __adl_begin<_Tp>
       |    ~~~^~~~~~~~~~~~~~~~
 In file included from /usr/include/c++/10/compare:39,
                  from /usr/include/c++/10/bits/stl_pair.h:65,
                  from /usr/include/c++/10/bits/stl_algobase.h:64,
                  from /usr/include/c++/10/bits/char_traits.h:39,
                  from /usr/include/c++/10/string_view:41,
                  from file.h:4,
                  from file.cpp:1:
 /usr/include/c++/10/concepts:119:10:   required for the satisfaction of '__class_or_enum<typename std::remove_reference<_Tp>::type>' [with _Tp = char&]
 /usr/include/c++/10/bits/iterator_concepts.h:856:15:   required for the satisfaction of '__adl_begin<_Tp>' [with _Tp = char&]
 /usr/include/c++/10/concepts:120:41: note: no operand of the disjunction is satisfied
   120 |    = is_class_v<_Tp> || is_union_v<_Tp> || is_enum_v<_Tp>;
       |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
 /usr/include/c++/10/concepts:120:6: note: the operand 'is_class_v<_Tp>' is unsatisfied because
   120 |    = is_class_v<_Tp> || is_union_v<_Tp> || is_enum_v<_Tp>;
       |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 /usr/include/c++/10/concepts:119:10:   required for the satisfaction of '__class_or_enum<typename std::remove_reference<_Tp>::type>' [with _Tp = char&]
 /usr/include/c++/10/bits/iterator_concepts.h:856:15:   required for the satisfaction of '__adl_begin<_Tp>' [with _Tp = char&]
 /usr/include/c++/10/concepts:120:6: note: the expression 'is_class_v<_Tp> [with _Tp = char]' evaluated to 'false'
   120 |    = is_class_v<_Tp> || is_union_v<_Tp> || is_enum_v<_Tp>;
       |      ^~~~~~~~~~~~~~~
 /usr/include/c++/10/concepts:120:25: note: the operand 'is_union_v<_Tp>' is unsatisfied because
   120 |    = is_class_v<_Tp> || is_union_v<_Tp> || is_enum_v<_Tp>;
       |      ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 /usr/include/c++/10/concepts:119:10:   required for the satisfaction of '__class_or_enum<typename std::remove_reference<_Tp>::type>' [with _Tp = char&]
 /usr/include/c++/10/bits/iterator_concepts.h:856:15:   required for the satisfaction of '__adl_begin<_Tp>' [with _Tp = char&]
 /usr/include/c++/10/concepts:120:25: note: the expression 'is_union_v<_Tp> [with _Tp = char]' evaluated to 'false'
   120 |    = is_class_v<_Tp> || is_union_v<_Tp> || is_enum_v<_Tp>;
       |                         ^~~~~~~~~~~~~~~
 /usr/include/c++/10/concepts:120:44: note: the operand 'is_enum_v<_Tp>' is unsatisfied because
   120 |    = is_class_v<_Tp> || is_union_v<_Tp> || is_enum_v<_Tp>;
       |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~
 /usr/include/c++/10/concepts:119:10:   required for the satisfaction of '__class_or_enum<typename std::remove_reference<_Tp>::type>' [with _Tp = char&]
 /usr/include/c++/10/bits/iterator_concepts.h:856:15:   required for the satisfaction of '__adl_begin<_Tp>' [with _Tp = char&]
 /usr/include/c++/10/concepts:120:44: note: the expression 'is_enum_v<_Tp> [with _Tp = char]' evaluated to 'false'
   120 |    = is_class_v<_Tp> || is_union_v<_Tp> || is_enum_v<_Tp>;
       |                                            ^~~~~~~~~~~~~~
 In file included from /usr/include/c++/10/string_view:44,
                  from file.h:4,
                  from file.cpp:1:
 /usr/include/c++/10/bits/range_access.h: In instantiation of 'constexpr auto std::ranges::views::__adaptor::_RangeAdaptorClosure<_Callable>::operator()(_Range&&) const [with _Range = char; _Callable = std::ranges::views::<lambda(_Range&&)>]':
 file.cpp:87:21:   required from here
 /usr/include/c++/10/bits/range_access.h:864:13: note: the required expression 'std::ranges::__cust::end(__t)' is invalid, because
   864 |  ranges::end(__t);
       |  ~~~~~~~~~~~^~~~~
 /usr/include/c++/10/bits/range_access.h:864:13: error: no match for call to '(const std::ranges::__cust_access::_End) (char&)'
 /usr/include/c++/10/bits/range_access.h:453:2: note: candidate: 'constexpr auto std::ranges::__cust_access::_End::operator()(_Tp&&) const [with _Tp = char&]'
   453 |  operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp>())
       |  ^~~~~~~~
 /usr/include/c++/10/bits/range_access.h:453:2: note: constraints not satisfied
 /usr/include/c++/10/bits/range_access.h: In instantiation of 'constexpr auto std::ranges::__cust_access::_End::operator()(_Tp&&) const [with _Tp = char&]':
 /usr/include/c++/10/bits/range_access.h:864:13:   required from 'constexpr auto std::ranges::views::__adaptor::_RangeAdaptorClosure<_Callable>::operator()(_Range&&) const [with _Range = char; _Callable = std::ranges::views::<lambda(_Range&&)>]'
 file.cpp:87:21:   required from here
 /usr/include/c++/10/bits/range_access.h:453:2:   required by the constraints of 'template<class _Tp>  requires (__maybe_borrowed_range<_Tp>) && ((is_bounded_array_v<typename std::remove_reference<_Tp>::type>) || (__member_end<_Tp>) || (__adl_end<_Tp>)) constexpr auto std::ranges::__cust_access::_End::operator()(_Tp&&) const'
 /usr/include/c++/10/bits/range_access.h:451:2: note: no operand of the disjunction is satisfied
   450 |  requires is_bounded_array_v<remove_reference_t<_Tp>> || __member_end<_Tp>
       |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   451 |  || __adl_end<_Tp>
       |  ^~~~~~~~~~~~~~~~~
 /usr/include/c++/10/bits/range_access.h:450:11: note: the operand 'is_bounded_array_v<std::remove_reference_t<_Tp> >' is unsatisfied because
   450 |  requires is_bounded_array_v<remove_reference_t<_Tp>> || __member_end<_Tp>
       |           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   451 |  || __adl_end<_Tp>
       |  ~~~~~~~~~~~~~~~~~
 /usr/include/c++/10/bits/range_access.h:453:2:   required by the constraints of 'template<class _Tp>  requires (__maybe_borrowed_range<_Tp>) && ((is_bounded_array_v<typename std::remove_reference<_Tp>::type>) || (__member_end<_Tp>) || (__adl_end<_Tp>)) constexpr auto std::ranges::__cust_access::_End::operator()(_Tp&&) const'
 /usr/include/c++/10/bits/range_access.h:450:11: note: the expression 'is_bounded_array_v<typename std::remove_reference<_Tp>::type> [with _Tp = char&; _Tp = char&]' evaluated to 'false'
   450 |  requires is_bounded_array_v<remove_reference_t<_Tp>> || __member_end<_Tp>
       |           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 /usr/include/c++/10/bits/range_access.h:450:58: note: the operand '__member_end<_Tp>' is unsatisfied because
   450 |  requires is_bounded_array_v<remove_reference_t<_Tp>> || __member_end<_Tp>
       |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
   451 |  || __adl_end<_Tp>
       |  ~~~~~~~~~~~~~~~~~                                        
 /usr/include/c++/10/bits/range_access.h:416:15:   required for the satisfaction of '__member_end<_Tp>' [with _Tp = char&]
 /usr/include/c++/10/bits/range_access.h:416:30:   in requirements with 'char& __t'
 /usr/include/c++/10/bits/range_access.h:418:18: note: the required expression 'std::__detail::__decay_copy(__t.end())' is invalid, because
   418 |    { __decay_copy(__t.end()) }
       |      ~~~~~~~~~~~~^~~~~~~~~~~
 /usr/include/c++/10/bits/range_access.h:418:23: error: request for member 'end' in '__t', which is of non-class type 'char'
   418 |    { __decay_copy(__t.end()) }
       |                   ~~~~^~~
 /usr/include/c++/10/bits/range_access.h:451:5: note: the operand '__adl_end<_Tp>' is unsatisfied because
   450 |  requires is_bounded_array_v<remove_reference_t<_Tp>> || __member_end<_Tp>
       |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   451 |  || __adl_end<_Tp>
       |  ~~~^~~~~~~~~~~~~~
 In file included from /usr/include/c++/10/compare:39,
                  from /usr/include/c++/10/bits/stl_pair.h:65,
                  from /usr/include/c++/10/bits/stl_algobase.h:64,
                  from /usr/include/c++/10/bits/char_traits.h:39,
                  from /usr/include/c++/10/string_view:41,
                  from file.h:4,
                  from file.cpp:1:
 /usr/include/c++/10/concepts:119:10:   required for the satisfaction of '__class_or_enum<typename std::remove_reference<_Tp>::type>' [with _Tp = char&]
 /usr/include/c++/10/bits/range_access.h:426:15:   required for the satisfaction of '__adl_end<_Tp>' [with _Tp = char&]
 /usr/include/c++/10/concepts:120:41: note: no operand of the disjunction is satisfied
   120 |    = is_class_v<_Tp> || is_union_v<_Tp> || is_enum_v<_Tp>;
       |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
 /usr/include/c++/10/concepts:120:6: note: the operand 'is_class_v<_Tp>' is unsatisfied because
   120 |    = is_class_v<_Tp> || is_union_v<_Tp> || is_enum_v<_Tp>;
       |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 /usr/include/c++/10/concepts:119:10:   required for the satisfaction of '__class_or_enum<typename std::remove_reference<_Tp>::type>' [with _Tp = char&]
 /usr/include/c++/10/bits/range_access.h:426:15:   required for the satisfaction of '__adl_end<_Tp>' [with _Tp = char&]
 /usr/include/c++/10/concepts:120:6: note: the expression 'is_class_v<_Tp> [with _Tp = char]' evaluated to 'false'
   120 |    = is_class_v<_Tp> || is_union_v<_Tp> || is_enum_v<_Tp>;
       |      ^~~~~~~~~~~~~~~
 /usr/include/c++/10/concepts:120:25: note: the operand 'is_union_v<_Tp>' is unsatisfied because
   120 |    = is_class_v<_Tp> || is_union_v<_Tp> || is_enum_v<_Tp>;
       |      ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 /usr/include/c++/10/concepts:119:10:   required for the satisfaction of '__class_or_enum<typename std::remove_reference<_Tp>::type>' [with _Tp = char&]
 /usr/include/c++/10/bits/range_access.h:426:15:   required for the satisfaction of '__adl_end<_Tp>' [with _Tp = char&]
 /usr/include/c++/10/concepts:120:25: note: the expression 'is_union_v<_Tp> [with _Tp = char]' evaluated to 'false'
   120 |    = is_class_v<_Tp> || is_union_v<_Tp> || is_enum_v<_Tp>;
       |                         ^~~~~~~~~~~~~~~
 /usr/include/c++/10/concepts:120:44: note: the operand 'is_enum_v<_Tp>' is unsatisfied because
   120 |    = is_class_v<_Tp> || is_union_v<_Tp> || is_enum_v<_Tp>;
       |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~
 /usr/include/c++/10/concepts:119:10:   required for the satisfaction of '__class_or_enum<typename std::remove_reference<_Tp>::type>' [with _Tp = char&]
 /usr/include/c++/10/bits/range_access.h:426:15:   required for the satisfaction of '__adl_end<_Tp>' [with _Tp = char&]
 /usr/include/c++/10/concepts:120:44: note: the expression 'is_enum_v<_Tp> [with _Tp = char]' evaluated to 'false'
   120 |    = is_class_v<_Tp> || is_union_v<_Tp> || is_enum_v<_Tp>;
       |                                            ^~~~~~~~~~~~~~

有没有一种简单的方法可以使用带有 C++20 范围的定界符来连接这些字符串?是因为字符串是临时的吗?

C++23 采用 views::join_with, and P2328 made views::join 加入纯右值 non-view 范围的范围(这意味着在这种情况下我们不再需要 cache1),所以您的示例现在是 C++23 中的 well-formed(稍作修改):

/*const*/ auto result = data
    | std::views::reverse
    | std::views::transform([](auto byte) { return fmt::format("{:02x}", byte); })
    | std::views::join_with(':');

注意到 join_with_view 会在这种情况下在内部缓存纯右值范围,因此它不再是 const-iterable。

Demo (Use the implementation provided by the original paper)