The problem with the pattern-matching approach is that it will only ever work if for every single container you write a specialization. This is tedious work.
Instead you can rely on other properties:
- a container will necessarily be iterable over via
begin(c)
andend(c)
expressions - on top of this, an associative container will have a
::key_type
nested type, among others, as expressed in § 23.2.4 [associative.rqmts].
Therefore, we can whip up a classifier, based on tag dispatching:
inline constexpr auto is_container_impl(...) -> std::false_type {
return std::false_type{};
}
template <typename C>
constexpr auto is_container_impl(C const* c) ->
decltype(begin(*c), end(*c), std::true_type{})
{
return std::true_type{};
}
template <typename C>
constexpr auto is_container(C const& c) -> decltype(is_container_impl(&c)) {
return is_container_impl(&c);
}
inline constexpr auto is_associative_container_impl(...)
-> std::false_type
{ return std::false_type{}; }
template <typename C, typename = typename C::key_type>
constexpr auto is_associative_container_impl(C const*) -> std::true_type {
return std::true_type{};
}
template <typename C>
constexpr auto is_associative_container(C const& c)
-> decltype(is_associative_container_impl(&c))
{
return is_associative_container_impl(&c);
}
And now you can write "simple" code:
template <typename C>
void print_container(C const& c, std::false_type/*is_associative*/) {
}
template <typename C>
void print_container(C const& c, std::true_type/*is_associative*/) {
}
template <typename C>
void print_container(C const& c) {
return print_container(C, is_assocative_container(c));
}
Now, this might not be exactly what you wish for, because under this requirements a set
is an associative container, but its value is not a pair
, so you cannot print key: value
. You have to adapt the tag-dispatching to your needs.