Splitting std::string based on delimiter using only find and substr
Bug

If the original string doesn't have trailing delimiter, the code leaves out the last token.

Unqualified calls

Unqualified call is any function call which doesn't have preceding ::. People oppose using namespace std;. In my opinion, it is not as much of a problem if done carefully (for small programs or at function/block scope). What is dangerous though, is unqualified call. It can be very evil. Anyway, my recommendation is to just type std:: unless you want to have a fallback into standard library functions at the current scope. I know this sounds confusing, but may be as time passes by it will become clearer. 

Formatting

There are formatting conventions many people use to make code readable among themselves. Sometimes it helps to understand control flow, templates, pointers, etc. I don't have any strong opinions on any particular one though, as long as it is consistent. Formatting the code in question used is rather unusual. I use snake_case, for everything except template parameter names and concepts. The more common version though is CamelCase for type names and camelCase for variable names, functions. ALL_CAPS are reserved for macros, as they're quite evil.

Accepting by value

Although sometimes it makes sense, in the case shown in question, it doesn't. When something is used for read-only purposes, pass by const T&amp;. In this case, it might even make sense to take by std::string_view. At the moment the code copies the arguments, which may be very costly if strings are big.

Peculiar way of looping

count and i are at the moment sort of a flag, either true or false (it takes many values, but squashing i and count into one would produce the flag). It would be better to loop until find returns npos.

size_t

For indexing, size_t is usually used. Some people prefer ptrdiff_t. The advantage they have over int is that they will as big as indexable memory is (though max of ptrdiff_t might be less that max of size_t). int might not be big enough in some cases.

Small stuff

Use const auto&amp; if looping in read only mode (for (const auto&amp; e: x)). 

return 0 is redundant.

Don't use std::endl unless immediate printing is necessary. An example of immediate printing can be real-time games, where users need to see output immediately.

Don't put extra () anywhere unless compiler doesn't understand you. Most of the time they are redundant, in worst cases they produce bugs and dangling references.



My approach

I'm a lover of standard library algorithms. Here is a good  talk to give a starter if you know basic algorithms (Disclaimer: I'm not familiar with the speaker, though ACCU is a good conference). 



Lets develop the algorithm first.


prev_pos &lt;- start of the string
next_pos &lt;- next delimiter location in string
if next_pos is end of the string, go to 7
append {prev_pos, next_pos} to results
increase prev_pos by next_pos + delimiter.size().
Go to 2.
if prev_pos is not end of the string, add the remaining portion of the string to results.


(This is a very bad parody to Knuth's style)

From the algorithm, it is already clear that the variables prev_pos and next_pos are not well named. I left it out as example, to demonstrate when variables could be given a good names. Good algorithm is complemented by good names, and sometimes staggered by bad ones.



Now the implementation. When starting out with a language, it is very hard to find direct equivalent to actions in the algorithm. Fortunately, C++ allows coming close to it, though building blocks are in the standard library already. I recommend using some good, up-to-date documentation. I use cppreference.com . 

The first step is understanding iterators. I don't really have a good reference for them, but iterators are the glue that binds containers and algorithms in C++. For now, let's assume it is a pointer into the string. Every standard library container has begin() and end(). The former is iterator to the beginning of container, and the latter is one past the end iterator. In case of "word", begin would point to w, and end to whatever is after d.

The algorithm that searches for the occurrence of a sequence in a bigger sequence is std::search(). If "very long string" is std::searched for "string", it will return iterator pointing to s.

std::vector has emplace_back(), which constructs element in-place. It takes whatever element type's constructor takes (in this case std::string). In the code above, a string is created after substring, and only then copied into result. Using iterator pair will eliminate the unnecessary copies.

Code

#include &lt;algorithm&gt;
#include &lt;vector&gt;
#include &lt;iterator&gt;
#include &lt;iostream&gt;
#include &lt;string_view&gt;

std::vector&lt;std::string&gt; split_string(std::string_view content, std::string_view delimeter)
{
    std::vector&lt;std::string&gt; result;
    auto prev_pos = content.begin();
    auto next_pos = std::search(prev_pos, content.end(),
                               delimeter.begin(), delimeter.end());
    while (next_pos != content.end())
    {
        result.emplace_back(prev_pos, next_pos);
        prev_pos = next_pos + delimeter.size();
        next_pos = std::search(prev_pos, content.end(), 
                               delimeter.begin(), delimeter.end());
    }

    if (prev_pos != content.end())
    {
        result.emplace_back(prev_pos, content.end());
    }
    return result;
}

int main()
{
    auto tokens = split_string("just a bunch of words", " ");

    std::copy(tokens.begin(), tokens.end(),
             std::ostream_iterator&lt;std::string&gt;{std::cout, ", "});
}


Demo.

Printing is done by copying the results into std::cout. Not everybody likes this, so just looping

for (const auto&amp; token: tokens)


will do too.



Some readers might ask a question "Why not return std::vector&lt;std::string_view&gt;"? The answer is that the views might dangle if the conversion is done from temporary string. A good example would be 

split_string(std::string{"string to split"}, "a");


The views would point to wherever the temporary is allocated, but after the function ends, the resulting views will dangle. One way to deal with this would be to create a function which copies, and name it split_string_copy, and let the current one to return views (hoping that IDE will put both version close, so developer would see the right one).



Yet another approach

Evangelists of iostream library might come up with a solution that modifies current locale, namely std::ctype&lt;char&gt;. Although it is really good at parsing simple CSV files, in a place where the function gets called multiple times it might be inefficient. Maybe in stateful cases it might prove as a better alternative.