Search code examples
c++std-rangesc++23

C++ views join_with using multi character separator


Can you use a multi character string as separator when using C++23 views::join_with ?

error message is returned by gcc 14.

#include <format>
#include <stdio.h>
#include <string>
#include <algorithm>
#include <ranges>
using namespace std;

int main(){
    const string a[2]={"6", "7"};
    const string buffer1 = a | views::join_with('\n') | ranges::to<string>(); //OK
    printf("%s", buffer1.c_str());

    const string buffer2 = a | views::join_with(",\n") | ranges::to<string>(); //KO
    printf("%s", buffer2.c_str());
    return 0;
}
prog.cc: In function 'int main()':
prog.cc:13:30: error: no match for 'operator|' (operand types are 'const std::string [2]' {aka 'const std::__cxx11::basic_string<char> [2]'} and 'std::ranges::views::__adaptor::_Partial<std::ranges::views::_JoinWith, const char*>')
   13 |     const string buffer2 = a | views::join_with(",\n") | ranges::to<string>(); //KO
      |                            ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~
      |                            |                   |
      |                            |                   std::ranges::views::__adaptor::_Partial<std::ranges::views::_JoinWith, const char*>
      |                            const std::string [2] {aka const std::__cxx11::basic_string<char> [2]}
In file included from prog.cc:5:
/opt/wandbox/gcc-head/include/c++/14.0.1/ranges:952:5: note: candidate: 'template<class _Self, class _Range>  requires (__is_range_adaptor_closure<_Self>) && (__adaptor_invocable<_Self, _Range>) constexpr auto std::ranges::views::__adaptor::operator|(_Range&&, _Self&&)'
  952 |     operator|(_Range&& __r, _Self&& __self)
      |     ^~~~~~~~
/opt/wandbox/gcc-head/include/c++/14.0.1/ranges:952:5: note:   template argument deduction/substitution failed:
/opt/wandbox/gcc-head/include/c++/14.0.1/ranges:952:5: note: constraints not satisfied
/opt/wandbox/gcc-head/include/c++/14.0.1/ranges: In substitution of 'template<class _Self, class _Range>  requires (__is_range_adaptor_closure<_Self>) && (__adaptor_invocable<_Self, _Range>) constexpr auto std::ranges::views::__adaptor::operator|(_Range&&, _Self&&) [with _Self = std::ranges::views::__adaptor::_Partial<std::ranges::views::_JoinWith, const char*>; _Range = const std::__cxx11::basic_string<char> (&)[2]]':
prog.cc:13:54:   required from here
/opt/wandbox/gcc-head/include/c++/14.0.1/ranges:952:5: note:    13 |     const string buffer2 = a | views::join_with(",\n") | ranges::to<string>(); //KO
/opt/wandbox/gcc-head/include/c++/14.0.1/ranges:952:5: note:       |                                                      ^
/opt/wandbox/gcc-head/include/c++/14.0.1/ranges:911:13:   required for the satisfaction of '__adaptor_invocable<_Self, _Range>' [with _Self = std::ranges::views::__adaptor::_Partial<std::ranges::views::_JoinWith, const char*>; _Range = const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > (&)[2]]
/opt/wandbox/gcc-head/include/c++/14.0.1/ranges:912:9:   in requirements  [with _Adaptor = std::ranges::views::__adaptor::_Partial<std::ranges::views::_JoinWith, const char*>; _Args = {const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > (&)[2]}]
/opt/wandbox/gcc-head/include/c++/14.0.1/ranges:912:44: note: the required expression 'declval<_Adaptor>()((declval<_Args>)()...)' is invalid
  912 |       = requires { std::declval<_Adaptor>()(declval<_Args>()...); };
      |                    ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
cc1plus: note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail
/opt/wandbox/gcc-head/include/c++/14.0.1/ranges:961:5: note: candidate: 'template<class _Lhs, class _Rhs>  requires (__is_range_adaptor_closure<_Lhs>) && (__is_range_adaptor_closure<_Rhs>) constexpr auto std::ranges::views::__adaptor::operator|(_Lhs&&, _Rhs&&)'
  961 |     operator|(_Lhs&& __lhs, _Rhs&& __rhs)
      |     ^~~~~~~~
/opt/wandbox/gcc-head/include/c++/14.0.1/ranges:961:5: note:   template argument deduction/substitution failed:
/opt/wandbox/gcc-head/include/c++/14.0.1/ranges:961:5: note: constraints not satisfied
/opt/wandbox/gcc-head/include/c++/14.0.1/ranges: In substitution of 'template<class _Lhs, class _Rhs>  requires (__is_range_adaptor_closure<_Lhs>) && (__is_range_adaptor_closure<_Rhs>) constexpr auto std::ranges::views::__adaptor::operator|(_Lhs&&, _Rhs&&) [with _Lhs = const std::__cxx11::basic_string<char> (&)[2]; _Rhs = std::ranges::views::__adaptor::_Partial<std::ranges::views::_JoinWith, const char*>]':
prog.cc:13:54:   required from here
/opt/wandbox/gcc-head/include/c++/14.0.1/ranges:961:5: note:    13 |     const string buffer2 = a | views::join_with(",\n") | ranges::to<string>(); //KO
/opt/wandbox/gcc-head/include/c++/14.0.1/ranges:961:5: note:       |                                                      ^
/opt/wandbox/gcc-head/include/c++/14.0.1/ranges:942:13:   required for the satisfaction of '__is_range_adaptor_closure<_Lhs>' [with _Lhs = const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > (&)[2]]
/opt/wandbox/gcc-head/include/c++/14.0.1/ranges:943:9:   in requirements with '_Tp __t' [with _Tp = const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > (&)[2]]
/opt/wandbox/gcc-head/include/c++/14.0.1/ranges:943:70: note: the required expression 'std::ranges::views::__adaptor::__is_range_adaptor_closure_fn(__t, __t)' is invalid
  943 |       = requires (_Tp __t) { __adaptor::__is_range_adaptor_closure_fn(__t, __t); };
      |                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
In file included from /opt/wandbox/gcc-head/include/c++/14.0.1/format:44,
                 from prog.cc:1:
/opt/wandbox/gcc-head/include/c++/14.0.1/charconv:626:3: note: candidate: 'constexpr std::chars_format std::operator|(chars_format, chars_format)'
  626 |   operator|(chars_format __lhs, chars_format __rhs) noexcept
      |   ^~~~~~~~
/opt/wandbox/gcc-head/include/c++/14.0.1/charconv:626:26: note:   no known conversion for argument 1 from 'const std::string [2]' {aka 'const std::__cxx11::basic_string<char> [2]'} to 'std::chars_format'
  626 |   operator|(chars_format __lhs, chars_format __rhs) noexcept
      |             ~~~~~~~~~~~~~^~~~~
In file included from /opt/wandbox/gcc-head/include/c++/14.0.1/bits/memory_resource.h:38,
                 from /opt/wandbox/gcc-head/include/c++/14.0.1/string:67,
                 from /opt/wandbox/gcc-head/include/c++/14.0.1/bits/locale_classes.h:40,
                 from /opt/wandbox/gcc-head/include/c++/14.0.1/locale:41,
                 from /opt/wandbox/gcc-head/include/c++/14.0.1/format:47:
/opt/wandbox/gcc-head/include/c++/14.0.1/cstddef:136:3: note: candidate: 'constexpr std::byte std::operator|(byte, byte)'
  136 |   operator|(byte __l, byte __r) noexcept
      |   ^~~~~~~~
/opt/wandbox/gcc-head/include/c++/14.0.1/cstddef:136:18: note:   no known conversion for argument 1 from 'const std::string [2]' {aka 'const std::__cxx11::basic_string<char> [2]'} to 'std::byte'
  136 |   operator|(byte __l, byte __r) noexcept
      |             ~~~~~^~~
In file included from /opt/wandbox/gcc-head/include/c++/14.0.1/bits/locale_facets.h:43,
                 from /opt/wandbox/gcc-head/include/c++/14.0.1/locale:42:
/opt/wandbox/gcc-head/include/c++/14.0.1/bits/ios_base.h:87:3: note: candidate: 'constexpr std::_Ios_Fmtflags std::operator|(_Ios_Fmtflags, _Ios_Fmtflags)'
   87 |   operator|(_Ios_Fmtflags __a, _Ios_Fmtflags __b)
      |   ^~~~~~~~
/opt/wandbox/gcc-head/include/c++/14.0.1/bits/ios_base.h:87:27: note:   no known conversion for argument 1 from 'const std::string [2]' {aka 'const std::__cxx11::basic_string<char> [2]'} to 'std::_Ios_Fmtflags'
   87 |   operator|(_Ios_Fmtflags __a, _Ios_Fmtflags __b)
      |             ~~~~~~~~~~~~~~^~~
/opt/wandbox/gcc-head/include/c++/14.0.1/bits/ios_base.h:130:3: note: candidate: 'constexpr std::_Ios_Openmode std::operator|(_Ios_Openmode, _Ios_Openmode)'
  130 |   operator|(_Ios_Openmode __a, _Ios_Openmode __b)
      |   ^~~~~~~~
/opt/wandbox/gcc-head/include/c++/14.0.1/bits/ios_base.h:130:27: note:   no known conversion for argument 1 from 'const std::string [2]' {aka 'const std::__cxx11::basic_string<char> [2]'} to 'std::_Ios_Openmode'
  130 |   operator|(_Ios_Openmode __a, _Ios_Openmode __b)
      |             ~~~~~~~~~~~~~~^~~
/opt/wandbox/gcc-head/include/c++/14.0.1/bits/ios_base.h:170:3: note: candidate: 'constexpr std::_Ios_Iostate std::operator|(_Ios_Iostate, _Ios_Iostate)'
  170 |   operator|(_Ios_Iostate __a, _Ios_Iostate __b)
      |   ^~~~~~~~
/opt/wandbox/gcc-head/include/c++/14.0.1/bits/ios_base.h:170:26: note:   no known conversion for argument 1 from 'const std::string [2]' {aka 'const std::__cxx11::basic_string<char> [2]'} to 'std::_Ios_Iostate'
  170 |   operator|(_Ios_Iostate __a, _Ios_Iostate __b)
      |             ~~~~~~~~~~~~~^~~

Solution

  • "..." is of type const char* but join_with requires the pattern with which to join to be either a ranges::forward_range or single object (cppreference). You can make use of std::literals to easily convert the "..." to std::strings or std::string_viewssv:

    using namespace std::literals;
    const std::string a[2]={"6", "7"};
    
    auto buffer1 = a | std::views::join_with('\n') | std::ranges::to<std::string>(); //OK
    
    auto buffer2 = a | std::views::join_with(",\n"s) | std::ranges::to<std::string>(); //OK
    
    auto buffer3 = a | std::views::join_with(",\n"sv) | std::ranges::to<std::string>(); //OK
    

    See demo