返回不提供back()
成员函数的容器中最后一个元素的最佳方法是什么,例如std::set
?
由于end()
方法在容器结束后将迭代器返回到第一个元素,因此在解除引用之前抓取最后一个元素以减少迭代器的唯一方法是什么?
如:
std::set<int> set = {1,2,3,4,5};
int end = *(set.end());
int beforeEnd = *(--set.end());
std::cout << "set.end() -> " << end << std::endl;
std::cout << "--set.end() -> " << beforeEnd << std::endl;
但是,这些都返回:
set.end() -> 5
--set.end() -> 5
这是获取最后一个元素的正确方法,为什么这些元素返回相同的值?
在解除引用之前,获取迭代器的唯一方法是抓取最后一个元素吗?
不,还有其他选择。
最简单的方法是使用反向迭代器(std::set::rbegin
或std::set::crbegin
),它直接给你一个超过std::set
s结束迭代器的元素。
来自cppreference.com,std::set::rbegin and std::set::crbegin
返回反向容器的第一个元素的反向迭代器。它对应于非反转容器的最后一个元素。如果容器为空,则返回的迭代器等于rend()。
std::set<int> set = { 1,2,3,4,5 };
auto iter = set.rbegin();
const int beforeEndIter = *iter;
std::cout << "--set.end() -> " << beforeEndIter << '\n';
(更新)如果容器中的第二个最后一个元素(即两个结束迭代器之后),使用std::next的原因与std::prev
的另一个答案中提到的相同。 See a demo
std::set<int> set = {1, 2};
const bool hasElements = set.cbegin() != set.cend();
auto iter = set.rbegin();
if(hasElements && iter != set.rend()) std::cout << "--set.end() -> " << *iter << '\n';
if(hasElements && std::next(iter) != set.rend()) std::cout << "two past set.end() -> " << *std::next(iter) << '\n';
输出:
--set.end() -> 2
two past set.end() -> 1
另一个答案中提到了第二种选择。
另一方面,取消引用end迭代器是一个undefined behavior,你可以期待任何结果。在您的情况下,您已获得容器的最后一个元素(即一个结束迭代器)。
这个
int end = *(set.end());
由πάντα ῥεῖ评论,具有未定义的行为。那是因为std::set::end
返回容器的最后一个元素后面的元素的迭代器。该元素充当占位符;尝试访问它会导致未定义的行为。 (https://en.cppreference.com/w/cpp/container/set/end,强调我的)
另一行:
int beforeEnd = *(--set.end());
它不能保证工作。参见例如qazxsw poi,强调我的:
虽然表达式https://en.cppreference.com/w/cpp/iterator/prev经常编译,但不能保证这样做:
--c.end()
是一个右值表达式,并且没有迭代器要求指定rvalue的减量保证可以工作。特别是,当迭代器被实现为指针时,c.end()
不会编译,而--c.end()
会编译。
所以它可能会因为无法编译而失败:
std::prev(c.end())
您可以编写类似下面的内容。
int arr[4] = {1,2,3,4};
int *p = --(arr + 4); // --> error: expression is not assignable