测试迭代器是否指向最后一项?

Wil*_*mKF 30 c++ iterator stl

我有一个由std :: find()生成的stl迭代器,并希望测试它是否是最后一个元素.写这个的一种方法如下:

mine *match = someValue;
vector<mine *> Mine(someContent);
vector<mine *>::iterator itr = std::find(Mine.begin(), Mine.end(), match);

if (itr == --Mine.end()) {
  doSomething;
}
Run Code Online (Sandbox Code Playgroud)

但在我看来,递减end()迭代器是在寻求麻烦,例如如果向量没有元素,那么它将是未定义的.即使我知道它永远不会是空的,它仍然看起来很难看.我想也许rbegin()是可行的方法,但我不确定将前向迭代器与反向迭代器进行比较的最佳方法.

GMa*_*ckG 55

做这个:

// defined in boost/utility.hpp, by the way
template <typename Iter>
Iter next(Iter iter)
{
    return ++iter;
}

// first check we aren't going to kill ourselves
// then check if the iterator after itr is the end
if ((itr != Mine.end()) && (next(itr) == Mine.end()))
{
    // points at the last element
}
Run Code Online (Sandbox Code Playgroud)

就这些.永远不会给你未定义的行为,适用于所有迭代器,美好的一天.

把它包起来玩得开心:

template <typename Iter, typename Cont>
bool is_last(Iter iter, const Cont& cont)
{
    return (iter != cont.end()) && (next(iter) == cont.end())
}
Run Code Online (Sandbox Code Playgroud)

赠送:

if (is_last(itr, Mine))
Run Code Online (Sandbox Code Playgroud)

如果您对实用功能/漂亮的代码过敏,请执行以下操作:

if ((itr != Mine.end()) && (itr + 1 == Mine.end()))
Run Code Online (Sandbox Code Playgroud)

但是你不能在非随机访问迭代器上做到这一点.这个适用于双向迭代器:

if ((itr != Mine.end()) && (itr == --Mine.end()))
Run Code Online (Sandbox Code Playgroud)

end() > itr第一次检查开始就是安全的.


Pot*_*ter 11

是的,end如果向量可能为空,则递减(或递增)是不安全的.用指针做同样的事情甚至有些不安全,尽管你可能会侥幸逃脱它.

为了确保安全,请使用已知安全有效的减法和值:

if ( Mine.end() - itr == 1 )
Run Code Online (Sandbox Code Playgroud)

为了兼容所有前向迭代器(例如in slist,而不是随机访问vector和的迭代器deque),请使用

if ( std::distance( itr, Mine.end() ) == 1 )
Run Code Online (Sandbox Code Playgroud)

或者如果你关心性能但是有双向迭代器(包括任何C++ 03容器)

if ( itr != Mine.end() && itr == -- Mine.end() )
Run Code Online (Sandbox Code Playgroud)

或者只有前向迭代器和O(1)时间的真正肛门案例,

if ( itr != Mine.end() && ++ container::iterator( itr ) == Mine.end() )
Run Code Online (Sandbox Code Playgroud)

或者如果你坚持聪明以避免命名迭代器类,

if ( itr != Mine.end() && ++ ( Mine.begin() = itr ) == Mine.end() )
Run Code Online (Sandbox Code Playgroud)


Mar*_*k B 5

为什么只有当项目是最后一项时才需要做特殊行为?

那这个呢.该计划只是将迭代器项目的地址与容器中最后一项的地址进行比较,并检查以确保该项目实际上不是结束(使back呼叫安全):

if (itr != Mine.end() && &*itr == &Mine.back()) {
  doSomething;
}
Run Code Online (Sandbox Code Playgroud)


Nik*_*iou 5

您首先需要一种方法来确定迭代器是否是反向迭代器这里巧妙地展示了这一点:

#include <iterator>
#include <type_traits>

template<typename Iter>
struct is_reverse_iterator : std::false_type { };

template<typename Iter>
struct is_reverse_iterator<std::reverse_iterator<Iter>>
: std::integral_constant<bool, !is_reverse_iterator<Iter>::value>
{ };
Run Code Online (Sandbox Code Playgroud)

然后你可以有两种风格来执行测试

template<bool isRev> // for normal iterators
struct is_last_it
{
    template<typename It, typename Cont>
    static bool apply(It it, Cont const &cont)
    { // you need to test with .end()
        return it != cont.end() && ++it == cont.end();
    }
};

template<> // for reverse iterators
struct is_last_it<true>
{
    template<typename It, typename Cont>
    static bool apply(It it, Cont const &cont)
    { // you need to test with .rend()
        return it != cont.rend() && ++it == cont.rend();
    }
};
Run Code Online (Sandbox Code Playgroud)

并且接口功能单一

template<typename It, typename Cont>
bool is_last_iterator(It it, Cont const &cont)
{
    return is_last_it<is_reverse_iterator<It>::value>::apply(it, cont);
};
Run Code Online (Sandbox Code Playgroud)

然后对于每种类型的迭代器(反向/直接),您可以使用接口函数

int main()
{
    std::vector<int> v;
    v.push_back(1);

    auto it (v.begin()),  ite(v.end());   // normal iterators
    auto rit(v.rbegin()), rite(v.rend()); // reverse iterators

    std::cout << is_last_iterator(it, v) << std::endl;
    std::cout << is_last_iterator(ite, v) << std::endl;
    std::cout << is_last_iterator(rit, v) << std::endl;
    std::cout << is_last_iterator(rite, v) << std::endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

请注意,某些实现(除了足够常见的std::begin()和之外,还包括和。如果可能,请使用这组函数而不是成员等。std::end()std::rbegin()std::rend().begin()