我最近开始更喜欢自由函数std::next
和std::prev
来显式复制和递增/递减迭代器。现在,我在一个非常具体的案例中看到了奇怪的行为,我很感激任何帮助揭开它的神秘面纱。
我有一个插值/外推函数在某boost::any_range
的{{1}}上运行。范围类型的完整定义是:
X_type
在此特定情况下,boost::any_range <
const X_type,
boost::random_access_traversal_tag,
const X_type,
std::ptrdiff_t
>
是从any_range
分配的,其中包含指向iterator_range
的两个指针,该const X_type
用作X_type
视图的大约一半{ {1}} data()
的区域。
在MSVC 2010中编译我的应用程序,一切正常。 在MinGW g ++ 4.7.0中编译相同的代码,它似乎挂在一个特定的位置,然后我缩小到这个(稍微缩写):
vector<char>
单步执行gdb中的代码,我发现它没有被卡住,只需要花费很长时间从单// Previously ensured conditions:
// 1) xrange is nonempty;
// 2) yrange is the same size as xrange.
auto x_equal_or_greater =
std::lower_bound(std::begin(xrange),std::end(xrange),xval);
if (x_equal_or_greater == std::end(xrange))
{
return *yit_from_xit(std::prev(x_equal_or_greater),xrange,yrange);
}
次调用返回 - 这在libstdc ++中以{{1}的方式实现}最终是std::prev
运算符。
仅将std::advance
行替换为:
+=
表现再次出色,几乎没有延迟。
我知道使用类型擦除迭代器(return
的那些)的开销,但即便如此,上面两种情况真的应该承担这样不同的成本吗?或者我做错了什么?
答案 0 :(得分:3)
好的,在回复SplinterOfChaos's comment后,我意识到了什么。问题出在你使用any_range上。特别是,第三个参数,表示Reference参数是const int
。在boost迭代器外观中,当引用不是真正的引用时,它将使用std::input_iterator_tag
,或者不提供STL等效标记。
这与事实有关,严格来说,所有前向,双向和随机访问STL迭代器必须使用真实引用作为其引用类型。从C ++ 11标准的24.2.5开始:
如果,类或内置类型X满足前向迭代器的要求 - X满足输入迭代器(24.2.3),
的要求 - X满足DefaultConstructible要求(17.6.3.1),
- 如果X是可变迭代器,则引用是对T的引用;如果X是常量迭代器,则引用是对const T ,
的引用 - 表109中的表达式是有效的,并具有指示的语义,
- X型对象提供多次通过保证,如下所述。
在这种情况下,当查询std::input_iterator_tag
时,它会返回iterator_category
,这会导致调用std::prev()
转入未定义的行为。
无论哪种方式,解决方案是将boost::any_range
的使用更改(如果可能)到以下内容:
boost::any_range <
const X_type,
boost::random_access_traversal_tag,
const X_type&,
std::ptrdiff_t
>
这将使其iterator_category
std::random_access_iterator_tag
,并按原先的预期执行操作。