Search code examples
c++stlc++20std-rangescompiler-specific

`std::views::split` on MSVC


I want to split a string by a token with std::views::split, and for each retrieved substring invoke the std::from_chars function. Here is an MRE (https://godbolt.org/z/1K71qo9s4) that compiles successfully on GCC but fails to compile on MSVC:

#include <iostream>
#include <ranges>
#include <string>
#include <charconv>
 
 
int main()
{
    std::string source{"10%15%20%35"};
    for ( auto i : source | std::views::split('%') )
    {
        int x;
        std::from_chars( i.begin().base(), i.end().base(), x );
        std::cout << x << ' ';
    }
}

What strange is that according to cppreference, the behavior of std::views::split was changed in P2210R2, providing base() function which effect is

Equivalent to return cur_

Then, according to compiler support page, the P2210R2 is supported in MSVC since 19.31, so the example should work.


Solution

  • If this were working how you think it does, i.begin().base() should be a std::string::iterator, which has to be const char* (or at least convertible to a const char*) to pass to std::from_chars.

    Looks like libstdc++ also doesn't implement this properly either, and i.begin() is returning a std::string::iterator instead of a std::ranges::views::split_view<std::string>::iterator. It just so happens that libstdc++'s std::string::iterator has a function called base() that returns a pointer.

    You have to get a const char* pointer anyways, which you can do with std::to_address:

        std::from_chars( std::to_address(i.begin()), std::to_address(i.end()), x );
    

    Or something like:

        std::from_chars( std::ranges::data(i), std::ranges::data(i) + std::ranges::size(i), x );
    
    // Or, since span is constructible from ranges
    
    for (std::span<const char> i : source | std::views::split('%')) {
        std::from_chars( i.data(), i.data() + i.size(), x );