我正在实现一个简单的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);
}

但是,当我要使用第一个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).

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

最佳答案

不幸的是,完全正确地执行此操作是一个问题。但是,您可以做一些合理的事情(在这种情况下会起作用)。基本上,您需要根据推导的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 {};

总而言之,该结构将确保可以使用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)
...

如果您感觉很生气,那么实际上您可以遍历整个Input Iterator需求列表,据我所知,构建一个特征来检测每个特征是否存在,然后最后进行合取以进行完全正确的检测以确保InputIt符合Input Iterator概念。不过这很痛苦。

关于c++ - 编译器如何区分 “vector::insert”的两个变体?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45581142/

10-11 22:38