Home > OS >  error: no matching function for call to ‘take_view(std::stringstream&, long unsigned int)’
error: no matching function for call to ‘take_view(std::stringstream&, long unsigned int)’

Time:03-07

I want to extract a maximum of N 1 strings from a std::stringstream.

Currently, I have the following code (that needs to be fixed):

#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
#include <iterator>
#include <ranges>
#include <algorithm>


int main( )
{
    const std::string_view sv { "   @a hgs  -- " };
    const size_t expectedTokenCount { 4 };

    std::stringstream ss;
    ss << sv;

    std::vector< std::string > foundTokens;
    foundTokens.reserve( expectedTokenCount   1 );

    std::ranges::for_each( std::ranges::take_view { ss, expectedTokenCount   1 }, [ &foundTokens ]( const std::string& token )
                                                                                  {
                                                                                    std::back_inserter( foundTokens );
                                                                                  } );

    if ( foundTokens.size( ) == expectedTokenCount )
    {
        // do something
    }

    for ( const auto& elem : foundTokens )
    {
        std::cout << std::quoted( elem ) << '\n';
    }
}

How should I fix it? Also, how should I use back_inserter to push_back the extracted strings into foundTokens?

CodePudding user response:

Note that the following aliases are in effect:

namespace views = std::views;
namespace rng = std::ranges;

There are a few issues and oddities here. First of all:

std::ranges::take_view { ss, expectedTokenCount   1 }

It's conventional to use the std::views API:

ss | views::take(expectedTokenCount   1)

The more glaring issue here is that ss is not a view or range. You need to create a proper view of it:

auto tokens = views::istream<std::string>(ss) | views::take(expectedTokenCount   1);

Now for the other issue:

std::back_inserter( foundTokens );

This is a no-op. It creates a back-inserter for the container, which is an iterator whose iteration causes push_back to be called, but doesn't then use it.

While the situation is poor in C 20 for creating a vector from a range or view, here's one way to do it:

rng::copy(tokens, std::back_inserter(foundTokens));

Putting this all together, you can see a live example, but note that it might not be 100% correct—it currently compiles with GCC, but not with Clang.

As noted below, you can also make use of views::split to split the source string directly if there's a consistent delimiter between the tokens:

std::string_view delim = " ";
auto tokens = views::split(sv, delim);

However, you might run into trouble if your standard library hasn't implemented this defect report.

  • Related