I have thought of a scenario where your version of the is_container trait runs into trouble.

If for example I derive my own type from std::basic_string, that type will now use be printed as a container because the template operator<< is now more specific than string's operator<<, which is probably not what you want.

Now I know you shouldn't derive from basic_string (it doesn't have a virtual destructor), but in general, if you have a type that has its own operator<<, and then derive from that type and the derived type matches your is_container trait, it will use the template operator<< while what you probably want is for it to use the base class operator<<.