views:

94

answers:

1

This is going to be rather long, so I apologize preemptively.

Near the end of Item 47 in Scott Meyers's "Effective C++" (Third Edition) he shows the following code:

    template<typename IterT, typename DistT>
    void advance(IterT& iter, DistT d)
    {
      doAdvance( iter, d, 
                 typename std::iterator_traits<IterT>::iterator_category() );
    }

He's already defined a number of overloaded doAdvance functions that take as their third argument std::random_access_iterator_tag, std::bidirectional_iterator_tag, or std::input_iterator_tag.

So here's my question: why is he appending () to std::iterator_traits::iterator_category? Initially I thought this was a typo (since it's not discussed at all in the main text) but then I saw that in Section 7.5.1 of Nicolai Josuttis's "The C++ Standard Library: A Tutorial and Reference" he has an example that for my purposes is equivalent:

template <class Iterator>
inline void foo (Iterator beg, Iterator end)
{
    foo ( beg, end,
         std::iterator_traits<Iterator>::iterator_category() );
}

OK, so it seems it's not a typo. When I see () next to a type name I think value-initialization. But I don't see why you would want to do that in this context. std::iterator_traits::iterator_category is simply a typedef for an iterator tag, and both Meyers and Josuttis show these to have the following hierarchy:

namespace std {
    struct output_iterator_tag {
    };
    struct input_iterator_tag {
    };
    struct forward_iterator_tag
     : public input_iterator_tag {
    };
    struct bidirectional_iterator_tag
     : public forward_iterator_tag {
    };
    struct random_access_iterator_tag
     : public bidirectional_iterator_tag {
    };
}

I then thought that maybe they're not showing the actual contents of the different iterator tags for brevity, so I looked at GCC's stl_iterator_base_types.h on my system and it looks identical. In other words, the only things in there are what the compiler will put in there.

I'm reaching the conclusion that it doesn't matter if you write std::iterator_traits::iterator_category or std::iterator_traits::iterator_category(), but since I'm not 100% sure I thought I'd ask the stackoverflow crowd. Thanks!

EDIT: I want to thank James for his answer which has (albeit indirectly) helped me realize what the source of my confusion was. The first of the doAdvance function templates looks like this:

template<typename IterT, typename DistT>        
void doAdvance(IterT& iter, DistT d,            
               std::random_access_iterator_tag) 
{
  iter += d;
}

The third parameter is a type unaccompanied by a variable name, i.e. it is an unnamed parameter. I had never seen an unnamed parameter in a function definition before; I think this is why I got confused. In this case having an unnamed parameter isn't a problem, since that argument is only used during the resolution of which overloaded function to use. Even so, when you call a function you need to pass an object, not a type.

Thus, it turns out that the value-initialization that Meyers and Josuttis used was necessary: they need to pass an object (even a temporary unnamed one), not a type.

+8  A: 
typename std::iterator_traits<IterT>::iterator_category()

This creates a value-initialized object of that iterator_category type, which you then pass to doAdvance().

The doAdvance() function is overloaded: there are overloads that take different iterator tags for their third parameter. This allows the most efficient method to be used to advance the iterator.

The overload that takes a random access iterator uses + to advance the iterator; the overload that takes a bidirectional iterator uses ++ in a loop to advance the iterator, etc.

For example, if you call doAdvance() and the iterator you pass it is only an input iterator, then its iterator_category will be derived from std::input_iterator_tag, and overload resolution will ensure that the doAdvance() overload that takes a std::input_iterator_tag will be called. The same goes for the other iterator tags.

James McNellis
OK, thanks. The reason I brought up the iterator tag hierarchy is that I wasn't sure if there were special default constructor defined in there that make the value-initialization less transparent. It seems there aren't, and therefore all we get are the synthesized default constructors.
Alexandros Gezerlis
I have updated my question, with what is essentially the answer (!). My initial conclusion (that it doesn't matter if you value-initialize or not) was wrong, but you didn't point that out (perhaps out of politeness). Even so, I'm going to go ahead and accept your answer, since it helped me realize where my confusion was.
Alexandros Gezerlis