用 std::min 将迭代器值钳位到 end()

Posted

技术标签:

【中文标题】用 std::min 将迭代器值钳位到 end()【英文标题】:Clamp iterator value to end() with std::min 【发布时间】:2017-08-23 22:14:02 【问题描述】:

我有一个包含 n 个字符串的向量。现在假设我想在地图中“分页”或“分组”这些字符串。

typedef std::vector<std::string> TStringVec;
TStringVec myVec;

//.. fill myVecwith n elements

typedef std::map<int, TStringVec>> TPagedMap;
TPagedMap myMap;

int ItemsPerPage = 3 // or whatever
int PagesRequired = std::ceil(myVec.size() / nItemsPerPage);
for (int Page = 0; Page < NumPagesMax; ++Page)

    TStringVec::const_iterator Begin = myVec.begin() + (ItemsPerPage * Page);
    TStringVec::const_iterator End = myVec.begin() + ItemsPerPage * (Page+1);
    myMap[Page] = TStringVec(Begin, End);

在这里很容易发现问题。在确定结束迭代器时,我冒着将分配的空间留给向量的风险。

简单示例:向量中有 5 个元素,ItemsPerPage 为 3。这意味着我们需要在地图中总共 2 个页面来对所有元素进行分组。

现在在进行最后一次迭代时,begin 指向 myVec[3],而 end 则“指向”myVec[6]。请记住,myVec 只有 5 个元素。

这种情况可以通过交换安全处理吗

TStringVec::const_iterator End = myVec.begin() + ItemsPerPage * (Page+1);

TStringVec::const_iterator End = std::min(myVec.begin() + ItemsPerPage * (Page+1), myVec.end()); 

它当然可以编译,而且它似乎可以工作。但我不确定这是否可以被认为是安全的事情。有什么建议或明确的答案吗?

我认为问题是......“过去”的值 .end() 是否保证大于 .end() 返回的地址?

提前致谢。

编辑:当然,事先检查if 可以解决问题,但我正在寻找更优雅 的解决方案。

【问题讨论】:

为什么要遍历页面,计算BeginEnd?为什么不遍历字符串,计算Page 嗯,你也会有同样的问题,不是吗? 不,因为您可以毫无危险地将页面添加到地图中。 那我不明白你的意思…… 如果您遍历页面并计算BeginEnd,您可能会取消引用超过向量末尾的指针,即UB。如果您遍历字符串并计算页面,则无法越过向量的末尾,并且您可以将页面添加到地图中而不会越过地图的末尾。 【参考方案1】:

您提议的替换有两点错误:

    您可能会在结束迭代器(即 UB)之后创建一个迭代器。 出于某种深不可测的原因,您想停在end() - 1??在其他任何地方,您都可以正确使用半开范围。

你想要的更像

auto End = myVec.cbegin() + std::min(ItemsPerPage * (Page + 1), myVec.size());

还请注意,我使用了auto 以避免不必要地指定复杂的类型名称。

顺便说一句,在整数上使用std::ceil 在概念上并不是很有用,至少编译器可能会优化通过double 的往返。

【讨论】:

广告 1:这就是我知道的问题 广告 2:感谢您指出这一点,纠正了错误。感谢您的补充意见,您是对的。但是我能保证使用 std::min 吗? 是的,应该这样做。 感谢您的快速帮助并含蓄地将我指向 cbegin()。我不知道它存在。【参考方案2】:

使用range-v3,您可以使用chunk 视图:

std::vector<std::string> myVec = /*...*/;

const int ItemsPerPage = 3 // or whatever

std::map<int, std::vector<std::string>>> myMap;
int counter = 0;
for (const auto& page : myVec | ranges::view::chunk(ItemsPerPage)) 
    myMap[counter++] = page;

Demo

【讨论】:

谢谢!我以前从未见过这种语法与 |虽然 您可以重载大多数运算符,包括operator |。这样做是为了实现直观的链接(而不是 f(g(a, h(x))) 我们有 x | h | g(a) | f)。 有道理。谢谢!

以上是关于用 std::min 将迭代器值钳位到 end()的主要内容,如果未能解决你的问题,请参考以下文章

钳位到系列中的下一个最小值

如何将字符串迭代器值添加到 ArrayAdapter?

传递迭代器值而不是引用函数

在 EventHandler 中使用 foreach 迭代器值 [重复]

为啥 g++ 需要 -fpermissive 标志来在 c++ 中打印迭代器值? [关闭]

如何遍历包含小于迭代器值的数组