使用std :: min

时间:2017-08-23 22:14:02

标签: c++ c++11

我有一个带有n个字符串的向量。现在让我们说我想" page"或" group"地图中的那些字符串。

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],但结束是&#34;指向&#34;到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()); 

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

我认为问题是......是一个价值&#34;过去&#34; .end()保证大于.end()返回的地址?

提前致谢。

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

2 个答案:

答案 0 :(得分:2)

您建议的更换有两个问题:

  1. 您可能会在结束迭代器(UB。
  2. )之外创建一个迭代器
  3. 出于某种不可思议的原因,你想停在end() - 1?在其他任何地方,你都可以正确地使用半开范围。
  4. 你想要的更像是

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

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

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

答案 1 :(得分: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