简体   繁体   English

为什么 Microsoft Visual C++ 库实现“解包”迭代器?

[英]Why does theMicrosoft Visual C++ library implementation 'unwrap' iterators?

Throughout microsofts stl implementation , pretty much all of their iterators are unwrapped before they are used.在整个微软的 stl 实现中,几乎所有的迭代器在使用之前都被解包

For instance, for_each looks like this:例如, for_each看起来像这样:

template <class _InIt, class _Fn>
_Fn for_each(_InIt _First, _InIt _Last, _Fn _Func) { // perform function for each element [_First, _Last)
    _Adl_verify_range(_First, _Last);
    auto _UFirst      = _Get_unwrapped(_First);
    const auto _ULast = _Get_unwrapped(_Last);
    for (; _UFirst != _ULast; ++_UFirst) {
        _Func(*_UFirst);
    }
    return _Func; }

_Adl_verify_range checks that first <= last , which I understand, but I dont quite understand the purpose of _Get_unwrapped(): _Adl_verify_range 检查first <= last ,我理解,但我不太明白 _Get_unwrapped() 的目的:

#if _HAS_IF_CONSTEXPR
template <class _Iter>
_NODISCARD constexpr decltype(auto) _Get_unwrapped(_Iter&& _It) {
    // unwrap an iterator previously subjected to _Adl_verify_range or otherwise validated
    if constexpr (is_pointer_v<decay_t<_Iter>>) { // special-case pointers and arrays
        return _It + 0;
    } else if constexpr (_Unwrappable_v<_Iter>) {
        return static_cast<_Iter&&>(_It)._Unwrapped();
    } else {
        return static_cast<_Iter&&>(_It);
    }
}
#else // ^^^ _HAS_IF_CONSTEXPR / !_HAS_IF_CONSTEXPR vvv
template <class _Iter, enable_if_t<_Unwrappable_v<_Iter>, int> = 0>
_NODISCARD constexpr decltype(auto) _Get_unwrapped(_Iter&& _It) {
    // unwrap an iterator previously subjected to _Adl_verify_range or otherwise validated
    return static_cast<_Iter&&>(_It)._Unwrapped();
}

It seems that it wants to decay the iterator, or cast it to an rvalue reference.似乎它想要衰减迭代器,或者将其转换为右值引用。

So my question is why does Visual++ use this paradigm?所以我的问题是为什么 Visual++ 使用这个范例? GCC does not do this as far as I can tell.据我所知,GCC 并没有这样做。

EDIT编辑

As requested, source of iterator._Unwrapped()根据要求, iterator._Unwrapped()的来源

_NODISCARD constexpr _Ptr _Unwrapped() const noexcept {
    return _Myptr;
}

_Myptr is defined in the iterator itself, and is just a raw pointer: _Myptr 在迭代器本身中定义,只是一个原始指针:

template <class _Ptr>
class unchecked_array_iterator {
...
private:
    _Ptr _Myptr; // underlying pointer
}

Why does VC++ wrap iterators?为什么 VC++ 包装迭代器?

It's a design choice.这是一个设计选择。 Indeed, for array-like types like std::array and std::vector an iterator can be a simple typedef to T* , which satisfies iterator semantics nicely, and indeed this is how GNU stdlibc++ implements it.实际上,对于像std::arraystd::vector这样的类数组类型,迭代器可以是T*的简单typedef ,它很好地满足了迭代器语义,这就是 GNU stdlibc++ 实现它的方式。 But from the standard perspective, iterator is a pointer-like object, but isn't necessarily a pointer.但是从标准的角度来看, iterator是一个类似指针的object,但不一定是指针。 So...所以...

  1. Assuming that it's a pointer would be a mistake and could lead to non-portable code ( case in point ).假设它是一个指针将是一个错误,并可能导致不可移植的代码( 例如)。
  2. Wrapping iterators allows for iterator debugging (see _ITERATOR_DEBUG_LEVEL ).包装迭代器允许迭代器调试(参见_ITERATOR_DEBUG_LEVEL )。 For example here's a debug operator++ :例如,这是一个调试operator++

     _CONSTEXPR17 _Array_const_iterator& operator++() { _STL_VERIFY(_Ptr, "cannot increment value-initialized array iterator"); _STL_VERIFY(_Idx < _Size, "cannot increment array iterator past end"); ++_Idx; return *this; }

Why does VC++ unwrap iterators?为什么 VC++ 解包迭代器?

  1. It's an optimization of error reporting.这是对错误报告的优化。 In loops like std::for_each , instead of getting a range error in the middle of for_each , the error is signaled upon entry into for_each .在像std::for_each这样的循环中,不是在for_each for_each发出错误信号。

  2. It's a performance optimization.这是一个性能优化。

    • In Debug mode, checking the preconditions once leads to faster code (debug mode is 10+ times slower already, so performance still matters even in debug mode).在调试模式下,检查前提条件一次会导致更快的代码(调试模式已经慢了 10 倍以上,因此即使在调试模式下性能仍然很重要)。
    • In Release mode the compiler can optimize pointer access better than inferring it via an iterator every time.在发布模式下,编译器可以优化指针访问,而不是每次都通过迭代器推断它。 Yes, it can still infer it in most cases, but an additional level of indirection can lead to different decision-making with regard to inlining elsewhere in the code.是的,在大多数情况下它仍然可以推断出来,但是额外的间接级别可能会导致关于代码中其他地方的内联的不同决策。

If I understand correctly, this is a debugging feature.如果我理解正确,这是一个调试功能。

The wrapped iterators contain additional information that allows the implementation to verify various preconditions, such as whether two iterators form a valid range, that an iterator has not been invalidated, that an iterator is used only with a container to whose element it refers.包装的迭代器包含允许实现验证各种先决条件的附加信息,例如两个迭代器是否形成有效范围,迭代器尚未失效,迭代器仅与它所引用的元素的容器一起使用。 _Adl_verify_range is one such check. _Adl_verify_range就是这样一种检查。

But then, when the iterator is actually used in the algorithm, the implementation does not want to incur the overhead of the verifications (which would happen for each ++ operation).但是,当在算法中实际使用迭代器时,实现不希望产生验证开销(每个++操作都会发生这种情况)。 It unwraps the iterator to a version that does not have the safety features.它将迭代器解包到不具有安全功能的版本。 Usually this means, that the unwrapped version is just a pointer (not necessarily a naked one, but wrapped in a class).通常这意味着,未包装的版本只是一个指针(不一定是裸指针,而是包装在一个类中)。

In Release builds, the algorithm itself can be optimized well because only very simple types are involved and no checks, but the initial safety checks can remain or can be omitted, as the developer wishes.在 Release 构建中,算法本身可以得到很好的优化,因为只涉及非常简单的类型并且没有检查,但是初始的安全检查可以保留或可以省略,如开发人员希望的那样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM