Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::vector constructor taking pair of iterators

Tags:

c++

c++11

stl

I am making some sort of container and I would like to mimic the interface of an std::vector. However, I am having a hard time understanding how the constructor overload (4) works. The problem is that it normally conflicts with overload (2).

// (2)
vector(size_type count, const T& value, const Allocator& alloc = Allocator());
// (4)
template<class InputIt>
vector(InputIt first, InputIt last, const Allocator& alloc = Allocator());

According to cppreference, until C++11 :

this constructor has the same effect as overload (2) if InputIt is an integral type.

I understand how it was done (tag dispatching or template specialization, I suppose), but I have no idea how the new behavior is achieved in C++11 :

This overload only participates in overload resolution if InputIt satisfies InputIterator, to avoid ambiguity with the overload (2).

Is that some sort of SFINAE trick ? I do not understand how SFINAE could work here. And, since concepts or not a thing in C++11 (nor C++14), I have no idea about how I could do that for my container.

Hence my question : how is it done in the standard library (or at least a guess), and how can I do it relatively easily for my container ?

like image 421
Nelfeal Avatar asked Feb 15 '15 18:02

Nelfeal


1 Answers

The way it's currently worded in the standard is rather interesting. [sequence.reqmts]/p15:

The extent to which an implementation determines that a type cannot be an input iterator is unspecified, except that as a minimum integral types shall not qualify as input iterators.

In other words, it's sufficient for implementations to test for integral types only, but they can do more if they want to.

With a SFINAE-friendly std::iterator_traits (voted into the C++17 working paper as an LWG issue, but probably provided by most implementations all along anyway), for example, one might test that std::iterator_traits<InputIterator>::iterator_category is valid and denotes a type derived from std::input_iterator_tag with something like

template<class InputIt, std::enable_if_t<std::is_base_of<std::input_iterator_tag,
                            typename std::iterator_traits<InputIterator>::iterator_category>::value, int> = 0>
vector(InputIt first, InputIt last, const Allocator& alloc = Allocator());

Note that this is just a proof-of-concept. Real implementations in standard libraries would probably be 1) more complex (for example, it may optimize based on iterator category - for forward iterators or better, it can allocate memory for all the elements in one go) and 2) even uglier than this and filled with underscores to avoid name conflicts.

like image 89
T.C. Avatar answered Nov 15 '22 18:11

T.C.