Dan Nissenbaum
Dan Nissenbaum

Reputation: 13928

Is {} a valid argument to pass to a function expecting an iterator (representing std::end() of some container)?

In boost directory_iterator example - how to list directory files not recursive (see this answer) is the sample code

#include <boost/filesystem.hpp>
#include <boost/range/iterator_range.hpp>
#include <iostream>

...
using namespace boost::filesystem;

for(auto& entry : boost::make_iterator_range(directory_iterator(p), {}))
{
    std::cout << entry << "\n";
}

(p is of type boost::filesystem::path.)

Upon looking at the documentation for make_iterator_range, I think the constructor being called is this one:

template< class ForwardTraversalIterator >
    iterator_range< ForwardTraversalIterator >
    make_iterator_range( ForwardTraversalIterator Begin,
                         ForwardTraversalIterator End );

If I'm right, then the second argument passed in the code example above, {}, seems to correspond to the end of whatever container is invisibly iterated over by the directory_iterator.

I've never seen this before.

Is it possible to construct the end iterator just by value-constructing such an iterator from the empty initializer list {}? (Am I even phrasing this properly?)

I wouldn't mind having someone spell out what is going on under the hood, given that the type of the iterator so constructed must match the type of the first iterator (directory_iterator(p)). (Is template argument deduction going on here?)

Upvotes: 4

Views: 395

Answers (2)

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385375

Yes, it's valid.

There's no template argument deduction beyond that which you already usually invoke: your first argument is of type directory_iterator, so the function is instantiated as such.

Following on from that, templates aside, now you are calling a function that takes two directory_iterators: the {} can only initialise a directory_iterator at that point, as that's what your function [template instance] takes. Hence, in this case, writing {} is functionally equivalent to writing directory_iterator{}.

If the directory_iterator could not be constructed from {}, your program would not compile.

Upvotes: 3

Drew Dormann
Drew Dormann

Reputation: 63946

Here are the various overloads for boost::make_iterator_range:

template< class ForwardTraversalIterator >
iterator_range< ForwardTraversalIterator >
make_iterator_range( ForwardTraversalIterator Begin, 
                     ForwardTraversalIterator End );

template< class ForwardRange >
iterator_range< typename range_iterator<ForwardRange>::type >
make_iterator_range( ForwardRange& r );

template< class ForwardRange >
iterator_range< typename range_iterator<const ForwardRange>::type >
make_iterator_range( const ForwardRange& r );

template< class Range >
iterator_range< typename range_iterator<Range>::type >
make_iterator_range( Range& r,
                     typename range_difference<Range>::type advance_begin,
                     typename range_difference<Range>::type advance_end );

template< class Range >
iterator_range< typename range_iterator<const Range>::type >
make_iterator_range( const Range& r, 
                     typename range_difference<const Range>::type advance_begin,
                     typename range_difference<const Range>::type advance_end );

Since your code has already specified that the first parameter is of type directory_iterator, the only valid type for the second parameter is directory_iterator. It's unambiguous.

Therefore, the type doesn't need to be specified. {} is synonymous with directory_iterator{}, which is also synonymous with directory_iterator().

Upvotes: 1

Related Questions