如果算法 std::mismatch 的两个范围的大小不同会产生未定义的行为?
Posted
技术标签:
【中文标题】如果算法 std::mismatch 的两个范围的大小不同会产生未定义的行为?【英文标题】:If the sizes of the two ranges to the algorithm std::mismatch different yields Undefined Behavior? 【发布时间】:2021-09-11 00:11:25 【问题描述】:我在 Cppreference 上看到了算法 std::mismatch
的可能实现:
template<class InputIt1, class InputIt2>
std::pair<InputIt1, InputIt2>
mismatch(InputIt1 first1, InputIt1 last1, InputIt2 first2)
while (first1 != last1 && *first1 == *first2)
++first1, ++first2;
return std::make_pair(first1, first2);
因此,如果第一个范围比第二个长,那么在某些时候,第二个范围中的越端迭代器将被取消引用,然后递增,从而产生未定义的行为。对?例如:
std::vector<int> v124, 10, 81, 7, 57;
std::vector<int> v224, 10, 81;
auto p = std::mismatch(v1.cbegin(), v1.cend(), v2.cbegin()); // UB?
所以元素是相等的,直到 v1
位于 7
并且 v2
是 past the last element
所以它被取消引用然后递增,从而导致 UB。
我认为算法也必须在每次迭代时检查第二个范围是否在末尾:
first1 != last1 && first2 != last2
我认为第三和第四个版本是正确的:
template<class InputIt1, class InputIt2>
std::pair<InputIt1, InputIt2> mismatch(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
while (first1 != last1 && first2 != last2 && *first1 == *first2)
++first1, ++first2;
return std::make_pair(first1, first2);
Fourth version
template<class InputIt1, class InputIt2, class BinaryPredicate>
std::pair<InputIt1, InputIt2> mismatch(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, BinaryPredicate p)
while (first1 != last1 && first2 != last2 && p(*first1, *first2))
++first1, ++first2;
return std::make_pair(first1, first2);
这里是cppreference主题的链接:
https://en.cppreference.com/w/cpp/algorithm/mismatch
【问题讨论】:
如果您不确定范围的长度是否相同,那就是 wgat 接受两对开始/结束迭代器的重载。 @ItachiUchiwa 他们采用 4 个迭代器[first1,last1)
和 [first2,last2)
算法 5、6、7 和 8。还有 "...如果未提供 last2(重载 (1-4) ),它表示 first2 + (last1 - first1)...." 因此,在调用 1 到 4 中的任何一个之前,您必须确保合同为真。因此范围 2 必须至少与范围一样长1.
@ItachiUchiwa 您应该使用 4 迭代器版本,除非您确定第一个范围不比第三个参数后面的范围长。如果您不确定,请始终使用 4 迭代器版本。
问题链接到的文档回答了问题。阅读返回值下的文字。
"我认为第三和第四个版本是对的:" -- 你说的“对”是什么意思?第三和第四个版本分别适用于实现重载 5 和 7,但不适用于实现其他重载。特别是对于重载 1,它们不可能正确,因为重载 1 需要三个参数,第三个版本需要四个参数,第四个版本需要五个参数。看起来你的“正确”概念会更好地针对声明而不是实现。
【参考方案1】:
是的,假定旧重载的范围相同。来自MSVC documentation:
在 C++14 代码中使用双范围重载,因为如果第二个范围比第一个范围长,则仅对第二个范围采用单个迭代器的重载将不会检测到差异,并且会导致未定义的行为,如果第二个范围比第一个范围短
使用双范围算法的性能损失最小(每个循环一次额外比较),并且对于随机访问迭代器,双范围 std::equals 和 std::is_permutation 需要在如果长度不同,则为常数时间。如果你有兴趣,在 CPPcon 上有一个谈话 by MSVC standard library implementer Stephan Lavavej from 2014 讨论这些变化,以及数字的“inner_product”函数和“transform”的二进制版本被遗漏了(并且仍然只有“range-and-”半'超载)。
如果双范围算法可用,则强烈推荐它们。仅当您知道自己在做什么并且绝对需要性能时才使用其他重载(然后通常仅使用非随机访问迭代器,因为编译器通常会通过检查来优化随机迭代器上的双范围重载(例如 is_permutation)长度,然后调用范围半重载)。
【讨论】:
以上是关于如果算法 std::mismatch 的两个范围的大小不同会产生未定义的行为?的主要内容,如果未能解决你的问题,请参考以下文章