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_reference
and difference_type
typenames, 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.