1

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.

i.e. 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.

Community
  • 1
  • 1
Marson Mao
  • 2,935
  • 6
  • 30
  • 45
  • Is the goal to keep the original `e` function(s), and to have a new `e` function that takes iterators? Then you might want to read about [`std::enable_if`](http://en.cppreference.com/w/cpp/types/enable_if), [type traits](http://en.cppreference.com/w/cpp/types) (and possibly [iterator traits](http://en.cppreference.com/w/cpp/iterator/iterator_traits)). – Some programmer dude Aug 13 '14 at 08:38
  • The goal is to keep call site looks like `extract::e(begin)`, so that `e` takes iterators. Modifying `e` to suit this goal is all welcome! – Marson Mao Aug 13 '14 at 08:40

2 Answers2

3

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.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Hi, sorry that I didnt make the question clear enough, is it possible to directly overload my `e` to take different type of iterators? I mean I can remove my current `e` and let new `e` to directly take iterators. – Marson Mao Aug 13 '14 at 08:45
  • I modified my question, if possible please read it again, thanks! – Marson Mao Aug 13 '14 at 08:47
  • It's not really possible to do what you seem to want, because a function template cannot deduce its arguments from a type like `std::vector::iterator` because `T` appears in a non-deducible context. That means you need to have a function template taking iterators like `e_it` above (but rename it to `e` if that makes you happier) and have that call the original `e` (rename it `e_impl` if that makes you happier) to distinguish between `pair` and other value types. – Jonathan Wakely Aug 13 '14 at 09:04
  • @JonathanWakely I see. Is there some reference to read about why it is non-deducible? (or google keyword is fine) I would like to know more detail, thanks! – Marson Mao Aug 13 '14 at 09:10
  • Yes, the term to search for is "non-deducible context". StackOverflow should explain it. – Jonathan Wakely Aug 13 '14 at 09:12
  • 1
    @MarsonMao: Just edited for a solution with only `e` and typetraits which matches the implementation of 40two. – Jarod42 Aug 13 '14 at 09:17
  • @JonathanWakely: OP doesn't want to retrieve the container, but dispatch according to the `value_type` of the iterator. – Jarod42 Aug 13 '14 at 09:18
  • Yes, I realise that, and you're right, it's possible with SFINAE, but ugly as sin. Your first suggestion is much cleaner – Jonathan Wakely Aug 13 '14 at 09:23
  • @Jarod42 Thanks again. Is the `is_pair` , `enable_if`, `iterator_traits`...etc necessary? I agree the second approach is less clear, that's why I tried to overload `e()` to make things easier. But I guess these features are used to against the type-deduce-problem mentioned by @JonathanWakely? – Marson Mao Aug 13 '14 at 09:23
  • I mean, the `iterator` can not be "easily" overloaded; it must be overloaded by using `enabl_if`...etc. If I want to make things easier then I should take the real value of `iterator` by `*` operator first; Is this correct? – Marson Mao Aug 13 '14 at 09:25
  • @MarsonMao: you can't deduce type for something like `e(map::iterator it)` (There may be an infinite possibilities, and in this case, in some implementation, there is as the COMP and ALLOCATOR are not needed for the iterator), so you have to rely on *type traits* or forwarding as in my first solution. – Jarod42 Aug 13 '14 at 09:43
  • @Jarod42 Thanks for the comment, I would do some more reading. – Marson Mao Aug 13 '14 at 09:49
0

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

101010
  • 41,839
  • 11
  • 94
  • 168