如何确定 STL 容器中的最后一个有效元素

问题描述 投票:0回答:9

如果我迭代 STL 容器,我有时需要知道当前项是否是序列中的最后一项。有没有更好的方法,然后做这样的事情?我可以以某种方式转换 rbegin() 吗?

std::vector<int> myList;

// ....
std::vector<int>::iterator lastit = myList.end();  
lastit--;

for(std::vector<int>::iterator it = myList.begin(); it != myList.end(); it++)  {
     if(it == lastit)
     {
        // Do something with last element
     }
     else
     {
       //  Do something with all other elements
     }
c++ stl
9个回答
3
投票

尝试以下方法

std::vector<int>::iterator it2 = (++it);
if ( it2 == myList.end() ) {
  ...
}

以下内容也应该有效

if ( it+1 == myList.end() ) {
  // it is last
  ...
}

2
投票

也许你可以向后迭代(使用

rbegin
/
rend
)并将特殊任务放在循环之前,或者用
it != lastit
替换结束检查并将特殊处理放在循环之后


1
投票

如果某些元素需要以不同方式处理,我会对我的设计产生一些疑问,但这个建议对我来说更干净一些(不要忘记测试空容器)

std::vector<int>::iterator lastit = myList.end();
if (lastit != myList.begin())
{
  lastit--;
  for(std::vector<int>::iterator it = myList.begin(); it != lastit; ++it)
  {
     // Do
  }
  // Do with last
}

1
投票

对于像向量这样的随机访问迭代器,您不需要临时的。你可以说:

if ( it + 1 == v.end() ) {
   // at one before end
}

编辑: 即使对于非随机访问类型,也可以使用 std:;distance:

if ( distance( it, v.end() ) == 1 ) {
   // at one before end
}

1
投票

使用反向迭代,这样你将只有一个end()-1式的计算(注意rbegin()+1)并且没有比较

for(vector<int>::iterator it = myValues.rbegin()+1; it != myValues.rend(); it++) {
    cout << *it << endl;
}
cout << "Process last one: " << *myValues.rbegin() << endl;

另外,对于向量<>,计算 end()-1 可能很快,所以你也可以像下面这样做:

for(vector<int>::iterator it = myValues.begin(); it != myValues.end()-1; it++) {
    cout << *it << endl;
}
cout << "Process last one: " << *myValues.rbegin() << endl;

如果不想处理循环后的元素,可以:

for(vector<int>::iterator it = myValues.rbegin(); it != myValues.rend(); it++) {
    if(it == myValues.rbegin())
        cout << "Process last one: " << *it << endl;
    else
        cout << *it << endl;
}

0
投票

一个重要的问题是:如果你对 1 个元素做了一些特殊的事情,为什么要创建一个循环。 为什么不对第三元素做一些特别的事情呢? 每四号一次? ...

只需迭代要同等对待的元素,编写单独的代码来对待其他元素。

也请查看这个问题的答案。


0
投票

为什么不:

if(!myList.empty())
    last_it = myList.begin() + myList.size()-1;
else
    last_it = myList.end();


//or
    last_it = myList.empty() ? myList.end() : myList.begin() + myList.size() - 1;

0
投票

我也经常遇到最后一个元素的问题。

经常会遇到这样的问题:要知道一个元素是否不是最后一个元素。

我也最喜欢这里匿名用户的变体。

if ( it + 1 == v.end() ) {
   // at one before end
}

这是一些对其他人也可能有用的替代方案。

不使用
it + 1
next

以下代码检测

i
是否“指向”最后一个元素,而不使用stl函数
next
。不使用
next
有一些运行时优势,特别是如果我们有一个向前迭代的成本更高的容器。

为了摆脱使用

next
,以下代码实现了这一点:

auto    iBegin = container.begin();
auto    iEnd = container.end();
for ( auto i = iBegin, iNext = iBegin; i != iEnd; i = iNext ) {
    iNext++;
    cout << *i;
    if ( iNext == iEnd ) {
        cout <<  "!" << endl;
    } else {
        cout <<  ", ";
    }
}

但是,我赞成创建一个适合所有四种情况的通用解决方案:

  • 仅最后一个元素并且
  • 仅第一个元素并且
  • 第一个和最后一个元素(当容器中只有元素时发生)
  • 既不是第一个也不是最后一个元素

通用解决方案

我尝试创建一个类似

for_each
的版本,可以使用 lambda / 闭包来处理这个问题。

梦想...?

如果我们可以像这样迭代会怎么样:

forEachConst( container, []( auto itVector, auto pos ){ 
        cout << (*itVector);
        if ( pos.isLast ) { 
            cout <<  "!" << endl;
        } else {
            cout <<  ", ";
        }
    }
);

...可能会实现吗?

这可以通过以下模板函数来实现:

class ForEachPos {
public:
    bool    isFirst;
    bool    isLast;
};

template< typename TIteratingContainer >
void forEachConst( const TIteratingContainer& iteratingContainer, std::function< void( typename TIteratingContainer::const_iterator&, const ForEachPos& ) > closure ) {
    typedef typename TIteratingContainer::const_iterator TIterator;
    ForEachPos    positionClass = ForEachPos{ true, false };
    TIterator               itBegin = iteratingContainer.begin();
    TIterator               itEnd = iteratingContainer.end();
    TIterator               itNext = itBegin;
    for ( TIterator it = itBegin; it != itEnd; it = itNext ) {
        itNext++;
        positionClass.isLast = ( itNext == iteratingContainer.end() );
        closure( it, positionClass );
        positionClass.isFirst = false;
    }
}

非常量版本甚至可以修改最后一个元素或最后一个和第一个元素的组合。

template< typename TIteratingContainer >
void forEach( TIteratingContainer& iteratingContainer, std::function< void( typename TIteratingContainer::iterator&, const ForEachPos& ) > closure ) {
    typedef typename TIteratingContainer::iterator TIterator;
    ForEachPos    positionClass = ForEachPos{ true, false };
    TIterator               itBegin = iteratingContainer.begin();
    TIterator               itEnd = iteratingContainer.end();
    TIterator               itNext = itBegin;
    for ( TIterator it = itBegin; it != itEnd; it = itNext ) {
        itNext++;
        positionClass.isLast = ( itNext == iteratingContainer.end() );
        closure( it, positionClass );
        positionClass.isFirst = false;
    }
}

访问上下文

为了访问上下文,可以相应地定义 lambda。例如。以下作品也适用:

string to_string( vector< int > vn ) {
    string  sList;
    sList = sList + "[";
    forEachConst( vn, [&sList]( auto itVector, auto pos ){ 
            if ( pos.isFirst ) { sList = sList + "("; }
            sList = sList + to_string(*itVector);
            if ( ! ( pos.isLast ) ) { sList = sList + ","; }
            if ( pos.isLast ) { sList = sList + ")"; }
        }
    );
    sList = sList + "]";
    return sList;
}

已测试

我使用

测试了代码
  • 一个空向量,
  • 和带有一个的向量
  • 两个和
  • 三要素。

兼容性

该代码至少适用于

-std=c++20

最后的话

设计缺陷?

一旦我处理列表中的特殊内容,我就会仔细检查这是否是一个设计缺陷。认为它一定是一个设计缺陷似乎不合理且不虔诚(就坚持某事而言)。这是我认为可读且可维护的代码示例:

forEachConst( container, []( auto itVector, auto pos ){ 
        cout << (*itVector);
        if ( pos.isLast ) { 
            cout <<  "!" << endl;
        } else {
            cout <<  ", ";
        }
    }
);

另请注意,以下内容并不等效,因为这里始终打印感叹号:

forEachConst( container, []( auto itVector, auto pos ){ 
        if ( ! pos.isFirst ) { 
            cout <<  ", ";
        }
        cout << (*itVector);
    }
);
cout <<  "!" << endl;

快吗?

该解决方案在每次迭代中不再使用

it + 1
next
。这可以节省一些 CPU 使用量,特别是如果
next
应用于容器,而
next
的 CPU 使用成本很高。另一方面,它同时也带来了一些更多的评估成本
positionClass.isFirst

可以改进吗?

您可能会发现这在通用性或性能方面可以得到改进。发表评论。谢谢。


-1
投票

如果您使用向量,那么使用整数索引进行迭代实际上要简单得多:

std::vector<int> myList;
for (unsigned int i = 0; i < myList.size(); i++)
{
  if (i == (myList.size() - 1))
  {
    processDifferently (myList[i])
  }
  else
  {
    process (myList[i])
  }
}

最小化对 myList.size() 的调用次数留给OP作为练习:)

© www.soinside.com 2019 - 2024. All rights reserved.