C++: How to pass any iterable type as a function parameter

All standard c++ collections has begin() and end() member functions. You could make use of that fact to ensure that the passed argument is actually a collection (in your terminology - iterable) by some SFINAE (c++11 example):

#include <array>
#include <list>
#include <vector>
#include <map>
#include <string>

template <class Iterable>
auto join(const std::string sep, const Iterable& iterable) -> decltype(iterable.begin(), iterable.end(), std::string{}) {
    (void)sep; // to suppress warning that sep isn't used
    // some implementation
    return {};
}

int main() {
    join(";", std::array<int, 5>{});
    join(";", std::list<int>{});
    join(";", std::vector<float>{});
    join(";", std::string{});
    join(";", std::map<int, float>{});
    //join(";", int{}); // does not compile as int is not a collection
}

[live demo]


In C++, rather than having one Iterable, we pass in an iterator (almost a pointer) to the front and the end of the range:

template<typename Iter>
std::string join(const std::string &sep, Iter begin, Iter end);

Note that the sep should be passed as const reference, as you don't need to copy it.

You don't need to worry about whether the Iter is actually an iterator, though. This is because the code will simply fail to compile if it doesn't work.

For example, suppose you implemented it like so (this is a bad implementation):

template<typename Iter>
std::string join(const std::string &sep, Iter begin, Iter end) {
    std::string result;

    while (begin != end) {
        result += *begin;
        ++begin;
        if (begin != end) result += sep;
    }

    return result;
}

Then the type passed in as Iter must have an operator++, an operator!=, and an operator* to work, which is the well understood contract of an iterator.