繁体   English   中英

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

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

在整个微软的 stl 实现中,几乎所有的迭代器在使用之前都被解包

例如, 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 检查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();
}

似乎它想要衰减迭代器,或者将其转换为右值引用。

所以我的问题是为什么 Visual++ 使用这个范例? 据我所知,GCC 并没有这样做。

编辑

根据要求, iterator._Unwrapped()的来源

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

_Myptr 在迭代器本身中定义,只是一个原始指针:

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

为什么 VC++ 包装迭代器?

这是一个设计选择。 实际上,对于像std::arraystd::vector这样的类数组类型,迭代器可以是T*的简单typedef ,它很好地满足了迭代器语义,这就是 GNU stdlibc++ 实现它的方式。 但是从标准的角度来看, iterator是一个类似指针的object,但不一定是指针。 所以...

  1. 假设它是一个指针将是一个错误,并可能导致不可移植的代码( 例如)。
  2. 包装迭代器允许迭代器调试(参见_ITERATOR_DEBUG_LEVEL )。 例如,这是一个调试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; }

为什么 VC++ 解包迭代器?

  1. 这是对错误报告的优化。 在像std::for_each这样的循环中,不是在for_each for_each发出错误信号。

  2. 这是一个性能优化。

    • 在调试模式下,检查前提条件一次会导致更快的代码(调试模式已经慢了 10 倍以上,因此即使在调试模式下性能仍然很重要)。
    • 在发布模式下,编译器可以优化指针访问,而不是每次都通过迭代器推断它。 是的,在大多数情况下它仍然可以推断出来,但是额外的间接级别可能会导致关于代码中其他地方的内联的不同决策。

如果我理解正确,这是一个调试功能。

包装的迭代器包含允许实现验证各种先决条件的附加信息,例如两个迭代器是否形成有效范围,迭代器尚未失效,迭代器仅与它所引用的元素的容器一起使用。 _Adl_verify_range就是这样一种检查。

但是,当在算法中实际使用迭代器时,实现不希望产生验证开销(每个++操作都会发生这种情况)。 它将迭代器解包到不具有安全功能的版本。 通常这意味着,未包装的版本只是一个指针(不一定是裸指针,而是包装在一个类中)。

在 Release 构建中,算法本身可以得到很好的优化,因为只涉及非常简单的类型并且没有检查,但是初始的安全检查可以保留或可以省略,如开发人员希望的那样。

暂无
暂无

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

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