std::vector 构造函数采用一对迭代器

std::vector constructor taking pair of iterators

我正在制作某种容器,我想模仿 std::vector. However, I am having a hard time understanding how the constructor overload (4) 作品的界面。问题是它通常与重载(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());

根据 cppreference,C++11 之前:

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

我了解它是如何完成的(我想是标签分派或模板特化),但我不知道 C++11 中的新行为是如何实现的:

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

这是某种 SFINAE 把戏吗?我不明白 SFINAE 怎么能在这里工作。而且,由于 C++11(也不是 C++14)中的概念与否,我不知道如何为我的容器做到这一点。

因此我的问题是:它是如何在标准库中完成的(或者至少是一个猜测),我如何才能相对轻松地为我的容器完成它?

标准中目前的措辞方式相当有趣。 [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.

换句话说,实现只测试整数类型就足够了,但如果他们愿意,他们可以做更多。

使用 SFINAE 友好的 std::iterator_traits(作为 LWG 问题投票进入 C++17 工作文件,但可能一直由大多数实现提供),例如,可以测试 std::iterator_traits<InputIterator>::iterator_category 是有效的,表示从 std::input_iterator_tag 派生的类型,类似于

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());

请注意,这只是概念验证。标准库中的实际实现可能 1) 更复杂(例如,它可能基于迭代器类别进行优化 - 对于前向迭代器或更好的迭代器,它可以一次性为所有元素分配内存)和 2) 甚至比这更丑陋和填充下划线以避免名称冲突。