Here is another c++14 solution inspired by previous answers. By defining enable_if_t, you can convert this to c++11 compatible code.
Tested on VS2019, and clang 6.0.
Checks implemented for type T:
- has typename T::value_type
- has typename T::iterator
- has typename T::const_iterator
- has typename T::size_type
- T::begin()returns value of type- T::iterator
- T::end()returns value of type- T::iterator
- T::cbegin()returns value of type- T::const_iterator
- T::cend()returns value of type- T::const_iterator
- T::size()returns value of type- T::size_type
Typename checks are implemented using void_t in the following way:
template<typename T, typename = void>
constexpr bool has_trait_xxx = false;
template<typename T>
constexpr bool has_trait_xxx<T, void_t<typename T::xxx>> = true;
Method return type checks are implemented using void_t + enable_if + is_same in the following way:
template<typename T, typename = void>
constexpr bool method_x_returns_type_y = false;
template<typename T>
constexpr bool method_x_returns_type_y<T, void_t<std::enable_if_t<is_same_v<decltype(std::declval<T>().x()), T::y>>>> = true;
Complete code for is_stl_container_like_v<T>:
template <typename... T>
using void_t = void;
template <typename T, typename U>
constexpr bool is_same_v = std::is_same<T, U>::value;
#define HAS_XXX_TRAIT_DEF(name)                                                                                    \
    template <typename T, typename = void>                                                                             \
    constexpr bool has_##name = false;                                                                                 \
    template <typename T>                                                                                              \
    constexpr bool has_##name<T, void_t<typename T::name>> = true;
HAS_XXX_TRAIT_DEF(value_type)
HAS_XXX_TRAIT_DEF(iterator)
HAS_XXX_TRAIT_DEF(const_iterator)
HAS_XXX_TRAIT_DEF(size_type)
template <typename T, typename = void>
constexpr bool is_iterable = false;
template <typename T>
constexpr bool
    is_iterable<T,
                void_t<std::enable_if_t<is_same_v<decltype(std::declval<T>().begin()), typename T::iterator>>,
                       std::enable_if_t<is_same_v<decltype(std::declval<T>().end()), typename T::iterator>>>> = true;
template <typename T, typename = void>
constexpr bool is_const_iterable = false;
template <typename T>
constexpr bool is_const_iterable<
    T,
    void_t<std::enable_if_t<is_same_v<decltype(std::declval<T>().cbegin()), typename T::const_iterator>>,
           std::enable_if_t<is_same_v<decltype(std::declval<T>().cend()), typename T::const_iterator>>>> = true;
template <typename T, typename = void>
constexpr bool is_sizeable = false;
template <typename T>
constexpr bool
    is_sizeable<T, void_t<std::enable_if_t<is_same_v<decltype(std::declval<T>().size()), typename T::size_type>>>> =
        true;
template <typename T>
constexpr bool is_stl_container_like_v = has_value_type<T> && has_iterator<T> && has_const_iterator<T> &&
                                         has_size_type<T> && is_iterable<T> && is_const_iterable<T> && is_sizeable<T>;
Example usage:
std::cout << is_stl_container_like_v<std::vector<int>> << "\n"; // 1
std::cout << is_stl_container_like_v<std::string> << "\n"; // 1
std::cout << is_stl_container_like_v<double> << "\n"; // 0
Problems with this implementation:
- references to container are not recognized as containers. e.g. std::vector<int>&
- does not check reference,const_referenceanddifference_typetypenames, and many other methods mentioned in named requirement Container
Thus, is_stl_container_like_v cannot be used to check whether a custom type meets the stl container requirement.