Like Marcelo's version, you must specialize the is_container template to inherit from true_type for each container type that you wish to use. I did a couple in the example, but you can easily add others. If you want to customize the delimiters, you similarly should specialize the delimiters template for that type.

So all it would take to support unordered_map is:

template<typename Key, typename Ty, typename Hash, typename Pred, typename Alloc>
struct is_container<std::unordered_map<Key, Ty, Has, Pred, Alloc> > : public std::true_type { };

The predefinitions aren't required for support. They are just there so that whatever header you put this is can declare is_container for list, map, vector, set, etc. without having to include <vector>, <list>, <set>, <map>, etc. if the user doesn't need them, which speeds up compilation times.

I'm not sure if using SFINAE to discover begin() and end() is possible, but doing that would have the disadvantage that it would also match std::string, which is probably not what you want.

EDIT: Your trait may work, but you probably want to check for const_iterator instead, and it will have the problem of also matching string, like I said.