简体   繁体   中英

Specialize function to print container contents

I was reading this question:

Specialize function for map like containers

And I tried to modify a little of 40two 's answer:

namespace extract
{
    template <typename T1, typename T2>
    const T2& e(const std::pair<T1, T2>& r)
    {
        return r.second;
    }

    template <typename T>
    const T& e(const T& r)
    {
        return r;
    }
}

template<typename Iterator>
void print(Iterator begin, Iterator end)
{
    while (begin != end)
    {
        std::cout << extract::e(*begin) << std::endl;
        ++begin;
    }
}

calling it like this, works fine:

std::vector<int> v(3,1);
std::map<int, int> m;
m[0]=10;
m[1]=11;
m[2]=12;
print(v.begin(), v.end());
print(m.begin(), m.end());

What I am wondering is, how to achieve same thing with only passing begin but not *begin ?

I would like to modify current e() functions to let them directly overload for different types of iterators.

ie to call it like:

template<typename Iterator>
void print(Iterator begin, Iterator end)
{
    while (begin != end)
    {
        // NOTE: only pass in begin, please modify e() to let it directly take iterator
        std::cout << extract::e(begin) << std::endl;
        ++begin;
    }
}

I think this would suit original question's need more, but I tried many ways and was not able to do it.

Thanks.

You may use

namespace extract
{
    // your current code for e

    template <typename IT>
    auto e_it(IT&& it) -> decltype (e(*it))
    {
        return e(*it);
    }
}

And change print to

template<typename Iterator>
void print(Iterator begin, Iterator end)
{
    while (begin != end)
    {
        std::cout << extract::e_it(begin) << std::endl;
        ++begin;
    }
}

Live example

Alternatively, you may do

template <typename Iter>
auto
e(Iter it)
-> typename std::enable_if<is_pair<typename std::iterator_traits<Iter>::value_type>::value,
    decltype(it->second)>::type
{
    return it->second;
}

template <typename Iter>
auto
e(Iter it)
-> typename std::enable_if<!is_pair<typename std::iterator_traits<Iter>::value_type>::value,
    decltype(*it)>::type
{
    return *it;
}

which seems less clear for me.

Live example .

You could define a is_iterator trait:

template <class, class Enable = void> struct is_iterator : std::false_type {};
template <typename T> struct is_iterator<T, typename std::enable_if<std::is_pointer<typename std::iterator_traits<T>::pointer>::value>::type> : std::true_type {};

and then with the help of SFINAE, to define the print_elem overloads as follows:

namespace detail {
  template<typename T1, typename T2>
  std::ostream& print_elem(std::ostream &out, std::pair<T1, T2> const &mp) {
    return (out << "(" << mp.first << ", " << mp.second << ")");
  }

  template<typename T>
  std::ostream& print_elem(std::ostream &out, T const &elem, typename std::enable_if<!is_iterator<T>::value>::type* = nullptr) {
    return (out << elem);
  }

  template<typename T>
  std::ostream& print_elem(std::ostream &out, T const &elem, typename std::enable_if<is_iterator<T>::value>::type* = nullptr) {
    return print_elem(out, *elem);
  }
}

LIVE DEMO

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM