编译器如何区分“ vector :: insert”的两个变体? [英] How does a compiler distinguish between two variations of "vector::insert"?

查看:64
本文介绍了编译器如何区分“ vector :: insert”的两个变体?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在实现一个简单的 std :: vector 。有两个 insert 函数:

I'm implementing a simple std::vector. There are two insert functions:

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 函数,编译器将调用第二个函数:

But when I want to use first insert function, the compiler invokes the second one:

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

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

Why compiler chooses the second function, and how to modify my code to make it right?

推荐答案

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

Unfortunately, doing this fully correctly is a problem. However, you can do something that is reasonable (and will work in this case). Basically, you need to conditionally enable the second overload dependent on whether the deduced type InputIt actually meets the requirements for input iterator. There's a whole list of input iterator requirements: http://en.cppreference.com/w/cpp/concept/InputIterator. However, we'll just focus on one that will solve this situation and most common cases for us. Namely, we'll verify that the type InputIt actually has a correct operator*. We use the void_t trick to build a trait for this:

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 {};

长短之处在于,该结构将确保<$ c $的实例可以使用 * 取消引用c> T ,并产生与 iterator_traits< T> :: reference 。完成此操作后,我们现在使用它来定义第二个重载:

The long and short of it is, that this struct will ensure that an instance of T can be dereferenced with * and yields the same type as iterator_traits<T>::reference. Having done that, we now use this to sfinae the second overload:

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概念。不过,这很痛苦。

If you are feeling frisky, you can in fact go through the entire list of Input Iterator requirements, and as far as I can see, build a trait that detects if each one is present, and then finally take the conjunction to do exactly correct detection to ensure that InputIt meets the Input Iterator concept. It's rather a pain though.

这篇关于编译器如何区分“ vector :: insert”的两个变体?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆