1

I already have this working code:

template <typename T1, typename T2>
std::ostream& operator<<(std::ostream &out, std::map<T1, T2> &map){
        for (auto it = map.begin(); it != map.end(); ++it) {
                out <<  it-> first << ", " << it->second << '\n';
        }
        return out;
}
template <typename T1, typename T2>
std::ostream& operator<<(std::ostream &out, std::unordered_map<T1, T2> &map){
        for (auto it = map.begin(); it != map.end(); ++it) {
                out <<  it-> first << ", " << it->second << '\n';
        }
        return out;
}

As you can see both functions are almost identical. Is there a way to remove one and use only one abstract function?

dv1729
  • 987
  • 1
  • 8
  • 25

2 Answers2

3

You can indeed: here is a single function template for any iterable type that has a value_type that is a std::pair<>. This will work with not only std::map<> and std::unordered_map<>, but also with std::vector<std::pair<>>, boost::container::list<std::pair<>>, etc.:

namespace detail {
    template<typename>
    struct is_pair : std::false_type { };

    template<typename T1, typename T2>
    struct is_pair<std::pair<T1, T2>> : std::true_type { };
}

template<
    // collection type
    typename T,
    // ensure value_type exists
    typename VT = typename T::value_type,
    // ensure value_type is some std::pair<>
    typename std::enable_if<detail::is_pair<VT>{}>::type* = nullptr
>
auto operator <<(std::ostream& out, T const& coll)
// ensure begin(coll) and end(coll) are legal
-> decltype(void(begin(coll)), void(end(coll)), out) {
    for (auto it = begin(coll); it != end(coll); ++it) {
        out << it->first << ", " << it->second << '\n';
    }
    return out;
}

Online Demo

ildjarn
  • 62,044
  • 9
  • 127
  • 211
  • Could you explain the solution a little bit more? Could you point me to some good resources to better understand C++ templates? – dv1729 Nov 15 '16 at 15:09
  • @dv1729 : You'll need to narrow down your request and clarify which aspect you need explained. [SFINAE](http://en.cppreference.com/w/cpp/language/sfinae) is the only technique being used here, and there are dozens of quality answers on SO explaining SFINAE in depth; [this](http://stackoverflow.com/q/3407633/636019) might be a good starting point (though it appears a bit dated). – ildjarn Nov 15 '16 at 15:20
0

Here is a demonstrative program

#include <iostream>
#include <map>
#include <type_traits>
#include <utility>

template <class T1, class T2, class T3, class T4,
          template <class T1, class T2, class T3, class T4> class Container>

std::ostream & operator<<( std::ostream &out, const Container<T1, T2, T3, T4> &c )
{
    static_assert( ( std::is_same<typename Container<T1, T2, T3, T4>::value_type, 
                     std::pair<const T1, T2>>::value ), "Invalid value type of the Container" );
    for ( const auto &p : c ) 
    {
        out <<  p.first << ", " << p.second << '\n';
    }

    return out;
}


int main() 
{
    std::map<int, char> m = 
    {
        { 65, 'A' }, { 66, 'B' }, { 67, 'C' }
    };

    std::cout << m << std::endl;

    std::multimap<int, char> mm = 
    {
        { 65, 'A' }, { 66, 'B' }, { 67, 'C' }
    };

    std::cout << mm << std::endl;

    return 0;
}

Its output is

65, A
66, B
67, C

65, A
66, B
67, C
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335