编译器如何区分"vector :: insert"的两个变体?

Sam*_*Sam 7 c++ stl c++11

我正在实施一个简单的std::vector.有两个insert功能:

template <typename T, typename Allocator>
typename Vector<T, Allocator>::iterator
Vector<T, Allocator>::insert(const_iterator pos, size_type count, const T& value)
{
    checkIterator(pos);
    auto p = const_cast<iterator>(pos);
    if (count == 0) {
        return p;
    }
    for (size_type i = 0; i < count; ++i) {
        p = insert(p, value);
    }
    return p;
}

template <typename T, typename Allocator>
template <typename InputIt>
typename Vector<T, Allocator>::iterator
Vector<T, Allocator>::insert(const_iterator pos, InputIt first, InputIt last)
{
    checkIterator(pos);
    auto p = const_cast<iterator>(pos);
    if (first == last) {
        return p;
    }
    for (auto iter = first; iter != last; ++iter) {
        p = insert(p, *iter);
        ++p;
    }
    return p - (last-first);
}
Run Code Online (Sandbox Code Playgroud)

但是当我想使用第一个insert函数时,编译器会调用第二个函数:

Vector<int> vi = {1, 2, 3};
vi.insert(vi.begin(), 3, 4); // get compile error, using insert(const_iterator pos, InputIt first, InputIt last).
Run Code Online (Sandbox Code Playgroud)

为什么编译器会选择第二个函数,以及如何修改我的代码以使其正确?

Nir*_*man 7

不幸的是,完全正确地完成这个是一个问题.但是,你可以做一些合理的事情(在这种情况下会起作用).基本上,您需要有条件地启用第二个重载,具体取决于推导类型是否InputIt实际满足输入迭代器的要求.有一个输入迭代器要求的完整列表:http://en.cppreference.com/w/cpp/concept/InputIterator.但是,我们只关注能够解决这种情况的问题以及最常见的情况.也就是说,我们将验证该类型InputIt实际上是否正确operator*.我们使用void_t技巧为此构建一个特征:

template <class ... T> using void_t = void;

template <class T, class = void>
struct has_iterator_deref : std::false_type {};

template <class T>
struct has_iterator_deref<T, std::enable_if_t<
    std::is_same<typename std::iterator_traits<T>::reference,
                 decltype(*std::declval<T>())>::value>> : std::true_type {};
Run Code Online (Sandbox Code Playgroud)

它的长短是这个结构将确保一个实例T可以解除引用*并产生相同的类型iterator_traits<T>::reference.完成后,我们现在使用它来解决第二个重载问题:

template <typename T, typename Allocator>
template <typename InputIt, class = enable_if_t<has_iterator_deref<T>::value>>
typename Vector<T, Allocator>::iterator
Vector<T, Allocator>::insert(const_iterator pos, InputIt first, InputIt last)
...
Run Code Online (Sandbox Code Playgroud)

如果您感觉活泼,您实际上可以浏览输入迭代器要求的整个列表,并且据我所知,构建一个特征,检测每个特征是否存在,然后最终使用连接进行完全正确的检测确保InputIt符合Input Iterator概念.虽然这很痛苦.