C++ 为什么微软Visual C++;图书馆实施';展开&x27;迭代器?
自始至终,几乎所有的迭代器在使用之前都是展开的 例如,如下所示:C++ 为什么微软Visual C++;图书馆实施';展开&x27;迭代器?,c++,visual-c++,stl,C++,Visual C++,Stl,自始至终,几乎所有的迭代器在使用之前都是展开的 例如,如下所示: 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_unwr
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; }
_Myptr是在迭代器本身中定义的,只是一个原始指针:
template <class _Ptr>
class unchecked_array_iterator {
...
private:
_Ptr _Myptr; // underlying pointer
}
模板
类未检查\u数组\u迭代器{
...
私人:
_Ptr _Myptr;//底层指针
}
为什么VC++包装迭代器?
这是一种设计选择。实际上,对于类似数组的类型,如std::array
和std::vector
,迭代器可以是一个简单的typedef
到T*
,这很好地满足了迭代器语义,并且确实是GNU stdlibc++实现它的方式。但是从标准的角度来看,迭代器是一个类似指针的对象,但不一定是指针。所以
假设它是指针将是一个错误,并可能导致不可移植代码()
包装迭代器允许迭代器调试(请参阅)。例如,这里有一个调试操作符+++
:
_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;
}
\u CONSTEXPR17\u数组\u const\u迭代器和运算符++(){
_STL_VERIFY(_Ptr,“无法增量值初始化数组迭代器”);
_STL_VERIFY(_Idx<_Size,“无法增加数组迭代器的结束”);
++_Idx;
归还*这个;
}
为什么VC++要展开迭代器?
这是对错误报告的优化。在循环<>代码> STD::FuyAuth/<代码>中,而不是在<代码>中的范围内获得范围错误<每一个< /代码>,输入“<代码> >每个<<代码> > 时,该错误被发出信号。
这是一个性能优化
- 在调试模式下,检查先决条件会导致更快的代码(调试模式已经慢了10多倍,所以即使在调试模式下,性能仍然很重要)
- 在发布模式下,编译器可以优化指针访问,而不是每次通过迭代器推断指针访问。是的,在大多数情况下它仍然可以推断,但是额外的间接级别可能会导致代码中其他地方的内联决策不同
如果我理解正确,这是一个调试功能
包装的迭代器包含其他信息,这些信息允许实现验证各种前提条件,例如两个迭代器是否构成有效范围、迭代器是否未失效、迭代器仅与它所引用的元素的容器一起使用。
\u Adl\u verify\u range
就是这样一种检查
但是,当在算法中实际使用迭代器时,实现不希望产生验证的开销(这将发生在每个++
操作中)。它将迭代器展开为不具有安全功能的版本。通常这意味着,未包装的版本只是一个指针(不一定是裸的,但包装在类中)
在发布版本中,算法本身可以很好地优化,因为只涉及非常简单的类型,并且没有检查,但是初始安全检查可以保留,也可以省略,正如开发人员所希望的那样。什么是\u Unwrapped()
?我猜MS迭代器有一些与调试相关的包装。这不会带来开销吗?我看到了constexpr
,那么您指的是什么开销呢?@PaulMcKenzie后一个版本不是constexpr,如果它不可用的话。即使使用constexpr,它也会有更多的编译时间。也许我应该更加小心我的措辞。我已经更新了这个问题,我主要感兴趣的是它为什么会这样做。@IgorR。我放弃了这个,因为它有点像兔子洞,我没有足够的经验来解释清楚。但是是的,这是内置在迭代器中的,它似乎只是调用_unfuncy(),它返回适当的指针。@rustyx我已经包含了它。如果是这样的话,他们一开始就费心制作迭代器包装器似乎有些奇怪。GCC只是使用T*作为stl数组的迭代器,而Visual++有一个自定义数组迭代器类。显然,他们构建了这个迭代器包装器,然后在每个要使用的函数中打开它。看起来很奇怪-当然,除非我错过了它确实有用的所有情况。在调试构建中,迭代器进行范围检查。如果迭代器是一个typedef的指针,那就不好了,这可能会导致不可移植的代码。因此,展开是对用户隐藏的实现细节。
template <class _Ptr>
class unchecked_array_iterator {
...
private:
_Ptr _Myptr; // underlying pointer
}
_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;
}