I'm writing some container manipulation functions. It is often the case that there's one version for things like vector-like containers such as vector, list, deque, array, etc. and another version for associative containers like map, multimap, unordered_map, etc. I was wondering what is the "best way" to detect whether a class is an associative container. Maybe something like detecting the existence of the mapped_type
typedef with BOOST_MPL_HAS_XXX_TRAIT_DEF
?
Asked
Active
Viewed 1,061 times
1

pythonic metaphor
- 10,296
- 18
- 68
- 110
3 Answers
1
It's a compile time test, so there's no CPU/memory efficiency aspect to select the "best way". If you have it working by checking mapped_type
with boost, and that suits your needs, there's no reason to look for anything different, though there are certainly boost-free alternatives (e.g. see here)
Note though that set
and unordered_set
are deemed associative containers by the Standard, but do not have a mapped_type
member - if you want to include them you could test for key_type
.

Community
- 1
- 1

Tony Delroy
- 102,968
- 15
- 177
- 252
-
I guess what I meant by 'best' was, the a method that did the best separation. As you point out, for example, checking for `mapped_type` doesn't get all associative containers in the standard. Also, I thought there might other meta-functions to check for the behavior of an associative container that would work on non-stl containers that have all the right interface to be associative containers without the typedef. Maybe that's 'more general'? – pythonic metaphor Mar 03 '14 at 21:13
-
Then you need to choose the API you'll program to... for example, do you care about having `begin`/`end` for iteration? Or just having `operator[](const key_type&)`. Same for `count`, `size()` etc.. The answer probably depends on what container manipulation functions you're going to write, but you haven't told us. – Tony Delroy Mar 03 '14 at 21:21
-
That's fair. I suppose deciding based on the existence of `key_type` is good enough. – pythonic metaphor Mar 03 '14 at 21:41
1
I would not go that way of assumption. Be specific and specialize a template.
I do this:
// is_deque
// ========
template<typename T, typename ... Types>
struct is_deque {
static constexpr bool value = false;
};
template<typename ... Types>
struct is_deque<std::deque<Types...>> {
static constexpr bool value = true;
};
// is_forward_list
// ===============
template<typename T, typename ... Types>
struct is_forward_list {
static constexpr bool value = false;
};
template<typename ... Types>
struct is_forward_list<std::forward_list<Types...>> {
static constexpr bool value = true;
};
// list
// ====
template<typename T, typename ... Types>
struct is_list {
static constexpr bool value = false;
};
template<typename ... Types>
struct is_list<std::list<Types...>> {
static constexpr bool value = true;
};
// vector
// ======
template<typename T, typename ... Types>
struct is_vector {
static constexpr bool value = false;
};
template<typename ... Types>
struct is_vector<std::vector<Types...>> {
static constexpr bool value = true;
};
// map
// ===
template<typename T, typename ... Types>
struct is_map {
static constexpr bool value = false;
};
template<typename ... Types>
struct is_map<std::map<Types...>> {
static constexpr bool value = true;
};
// set
// ===
template<typename T, typename ... Types>
struct is_set {
static constexpr bool value = false;
};
template<typename ... Types>
struct is_set<std::set<Types...>> {
static constexpr bool value = true;
};
// unordered_map
// =============
template<typename T, typename ... Types>
struct is_unordered_map {
static constexpr bool value = false;
};
template<typename ... Types>
struct is_unordered_map<std::unordered_map<Types...>> {
static constexpr bool value = true;
};
// unordered_set
// =============
template<typename T, typename ... Types>
struct is_unordered_set {
static constexpr bool value = false;
};
template<typename ... Types>
struct is_unordered_set<std::unordered_set<Types...>> {
static constexpr bool value = true;
};
// is_sequence_container
// =====================
template <typename T>
struct is_sequence_container {
static constexpr bool value
= is_deque<T>::value
|| is_forward_list<T>::value
|| is_list<T>::value
|| is_vector<T>::value;
};
// is_associative_container
// ========================
template <typename T>
struct is_associative_container {
static constexpr bool value
= is_map<T>::value
|| is_set<T>::value;
};
// is_unordered_associative_container
// ==================================
template <typename T>
struct is_unordered_associative_container {
static constexpr bool value
= is_unordered_map<T>::value
|| is_unordered_set<T>::value;
};
// is_container
// ============
template <typename T>
struct is_container {
static constexpr bool value
= is_sequence_container<T>::value
|| is_associative_container<T>::value
|| is_unordered_associative_container<T>::value;
};
0
I know that the question was asked 5 years ago, but this is what I did without any requirements beyond c++11
:
/// @brief container traits
////////////////////////////////////////////////////////////////////////////////
namespace container_traits {
using tc = char[2];
template<typename T> struct is_container {
static tc& test(...);
template <typename U>
static char test(U&&, decltype(std::begin(std::declval<U>()))* = 0);
static constexpr bool value = sizeof(test(std::declval<T>())) == 1;
};
template < typename T > struct is_associative {
static tc& test(...) ;
template < typename U >
static char test(U&&, typename U::key_type* = 0) ;
static constexpr bool value = sizeof( test( std::declval<T>() ) ) == 1 ;
};
}
template < typename T > struct is_container :
std::conditional<(container_traits::is_container<T>::value || std::is_array<T>::value)
&& !std::is_same<T, std::string>::value
&& !std::is_same<T, const std::string>::value, std::true_type, std::false_type >::type {};
template < typename T > struct is_associative :
std::conditional< container_traits::is_container<T>::value && container_traits::is_associative<T>::value, std::true_type, std::false_type >::type {};
////////////////////////////////////////////////////////////////////////////////
/// @brief no std::enable_if_t in c++11
////////////////////////////////////////////////////////////////////////////////
#if __cplusplus <= 201103L
namespace std {
template< bool B, class T = void >
using enable_if_t = typename std::enable_if<B,T>::type;
}
#endif

Kaveh Vahedipour
- 3,412
- 1
- 14
- 22