Writing a really generic solution for this is hard. The problem with checking an arbitrary type T against std::vector or std::array is that the latter are not classes, they are class templates. Even worse, std::array is a class template with a non-type template parameter, so you can't even have a parameter pack which will hold both std::vector and std::array.
You can get around this somewhat by explicitly wrapping non-type parameters up in types, but it gets ugly, fast.
Here is a solution I came up with that will support any class or template class with no non-type template parameters by default. Template classes with non-type template parameters can be supported by adding a wrapper type to map non-type parameters to type parameters.
namespace detail{ //checks if two types are instantiations of the same class template template<typename T, typename U> struct same_template_as: std::false_type {}; template<template<typename...> class X, typename... Y, typename... Z> struct same_template_as<X<Y...>, X<Z...>> : std::true_type {}; //this will be used to wrap template classes with non-type args template <typename T> struct wrapImpl { using type = T; }; //a wrapper for std::array template <typename T, typename N> struct ArrayWrapper; template <typename T, std::size_t N> struct ArrayWrapper<T, std::integral_constant<std::size_t, N>> { using type = std::array<T,N>; }; //maps std::array to the ArrayWrapper template <typename T, std::size_t N> struct wrapImpl<std::array<T,N>> { using type = ArrayWrapper<T,std::integral_constant<std::size_t,N>>; }; template <typename T> using wrap = typename wrapImpl<typename std::decay<T>::type>::type; //checks if a type is the same is one of the types in TList, //or is an instantiation of the same template as a type in TempTList //default case for when this is false template <typename T, typename TList, typename TempTList> struct one_of { using type = std::false_type; }; //still types in the first list to check, but the first one doesn't match template <typename T, typename First, typename... Ts, typename TempTList> struct one_of<T, std::tuple<First, Ts...>, TempTList> { using type = typename one_of<T, std::tuple<Ts...>, TempTList>::type; }; //type matches one in first list, return true template <typename T, typename... Ts, typename TempTList> struct one_of<T, std::tuple<T, Ts...>, TempTList> { using type = std::true_type; }; //first list finished, check second list template <typename T, typename FirstTemp, typename... TempTs> struct one_of<T, std::tuple<>, std::tuple<FirstTemp, TempTs...>> { //check if T is an instantiation of the same template as first in the list using type = typename std::conditional<same_template_as<wrap<FirstTemp>, T>::value, std::true_type, typename one_of<T, std::tuple<>, std::tuple<TempTs...>>::type>::type; }; } //top level usage template <typename T, typename... Ts> using one_of = typename detail::one_of<detail::wrap<T>,Ts...>::type; struct Foo{}; struct Bar{}; template <class Type> auto operator<< (std::ostream& stream, const Type subject) //is Type one of Foo or Bar, or an instantiation of std::vector or std::array -> typename std::enable_if< one_of<Type, std::tuple<Foo,Bar>, std::tuple<std::vector<int>,std::array<int,0>> >::value, std::ostream&>::type { stream << "whatever, derived from subject\n"; return stream; }
Please don't use this, it's horrible.
Live Demo
Type == TypeA or TypeB or TypeCdifferent from writing one overload for each?