-1

I want to implement a simple function that will split std::string based on the delimiter ,\s.

For example, split("Emerson, Lake, Palmer") should yield std::vector<std::string> of

Emerson
Lake
Palmer

I discovered, that it could be simply achieved with back_inserter() (the example is very helpful) . The function below iterates over supplied string and copies its content directly to a vector.

std::vector<std::string> split(const std::string& s) {
    std::vector<std::string> tokens;

    std::string::const_iterator from = s.begin();
    for (std::string::const_iterator it = s.begin(); it < s.end(); it++) {
        if ( *it == ',' ) {
            std::copy(from, it, std::back_inserter(tokens));
            from = it + 2;
        }
        if (it == s.end()-1)
            std::copy(from, it+1, std::back_inserter(tokens));
    }
    return tokens;
}

However, the code doesn't compile with the error

no match for 'operator=' (operand types are 'std::back_insert_iterator<std::vector<std::__cxx11::basic_string<char> > >' and 'const char')

I roughly understand what does it mean, however, I don't know how to go about this. Am I missing something simple to make it work, or the whole concept is not a way to go?

Maverick
  • 420
  • 5
  • 16
  • 1
    `tokens` is a `vector` so you can add strings: `tokens.emplace_back(std::string(from, it));` – 001 Jan 18 '21 at 22:05
  • This isn't a good use of `back_inserter`. Pair `back_inserter` with a function from that eats the input stream as an iterator and stores into a container iterator. – user4581301 Jan 18 '21 at 22:08
  • @Johny's answer gets the job done, however, I am curious what's wrong with this approach. @user4581301, I thought `std::copy` is such a function. – Maverick Jan 18 '21 at 22:12
  • @JohnnyMopp passing temporary to `emplace_back()` defeats whole purpose of such functions. you basically make it the same as `push_back()` – Slava Jan 18 '21 at 22:12
  • @Maverick yes `std::copy()` such a function but to use it here you need to iterate over something that has `std::sting` inside ie `*it` for iterator provides string, not individual symbol. – Slava Jan 18 '21 at 22:15
  • 1
    You know, you've got me there, @Maverick. I was looking at the question wrong. I knew you were using `std::copy` to gather characters into a string and that the back inserter was expecting a `string`, but the disjoint types totally ed up my brain. – user4581301 Jan 18 '21 at 22:19

2 Answers2

2

When you submit a range as 2 iterators to std::copy() you mean that you want to copy each element that such iterators point to to the target. But std::string::iterator points to individual chars and I do not think you want to copy each symbol into the vector separately. Instead you can create a std::string from pair and submit it to the vector:

for (std::string::const_iterator it = s.begin(); it < s.end(); it++) {
    if ( *it == ',' ) {
        tokens.emplace_back( from, it );
        from = it + 2;
    }
...
Slava
  • 43,454
  • 1
  • 47
  • 90
  • Isn't it the approach you criticized in the comments under the question? ;> Nevertheless, maybe there is just nothing better when one wants to stick to my initial idea. Thanks for the help! – Maverick Jan 18 '21 at 22:27
  • What approach do you mean that I criticized? Please elaborate. – Slava Jan 18 '21 at 22:29
  • that passing temporary to emplace_back() defeats the whole purpose of such functions. – Maverick Jan 18 '21 at 22:31
  • 1
    If you look carefully you will see the difference and you should notice that I do not pass temporary of `std::string` but submit 2 iterators directly. That the whole purpose of `emplace...` functions - they forward arguments to ctor. You can find details here https://stackoverflow.com/questions/50740699/copy-elision-when-creating-object-inside-emplace – Slava Jan 18 '21 at 22:36
  • Indeed, I overlooked that one. All is clear now. Have a good day – Maverick Jan 18 '21 at 22:39
1

In this statement

std::copy(from, it, std::back_inserter(tokens));

the iterator from supplies objects of the type char while the iterator of the type std::back_inserter expects an object of the type std::string.

Instead you could write at least

tokens.push_back( std::string( from, it ) );

or

tokens.push_back( { from, it } );
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335