【发布时间】:2011-10-20 15:42:41
【问题描述】:
目前,我只能使用以下方法进行基于范围的循环:
for (auto& value : values)
但有时我需要一个值的迭代器,而不是引用(无论出于何种原因)。有什么方法不用遍历整个向量比较值吗?
【问题讨论】:
标签: c++ c++11 for-loop iterator
目前,我只能使用以下方法进行基于范围的循环:
for (auto& value : values)
但有时我需要一个值的迭代器,而不是引用(无论出于何种原因)。有什么方法不用遍历整个向量比较值吗?
【问题讨论】:
标签: c++ c++11 for-loop iterator
一如既往地迟到:),但我在这里。
C++20 在基于范围的 for 循环中引入了 initializer-statement 的语法。这个初始化可能是一个simple-declaration,或者一个expression-statement。 (当前的 C++23 工作草案也使得写一个 type-alias-declaration 成为可能。
对于迭代器或索引,只需执行类似以下的操作:
std::vector<int> vec;
for (auto it = vec.begin(); auto& elem: vec) {
// ...
it++;
}
for (int i = 0; auto& elem: vec) {
// ...
i++;
}
这解决了@nawaz提到的外部变量方法的范围问题。
注意:这类表达式不仅限于一次初始化,还有很多很酷的事情可以内联完成。例子:
// This will only be useful for containing a complex typedef's scope inside
// a for-loop, and I would say, is a smell that your typing system is not too
// developed.
for(typedef std::vector<std::vector<int>> Matrix; Matrix& m: container) {
// ...
}
// Good old (or rather, very new) one liner.
for(MyType my_instance(x,y,z); auto& elem: my_instance) {
// ...
}
【讨论】:
让我们做的很肮脏...... 我知道,0x70h 会随着堆栈使用、编译器版本、...而变化。 它应该由编译器公开,但不是:-(
char* uRBP = 0; __asm { mov uRBP, rbp }
Iterator** __pBegin = (Iterator**)(uRBP+0x70);
for (auto& oEntry : *this) {
if (oEntry == *pVal) return (*__pBegin)->iPos;
}
【讨论】:
使用旧的for 循环作为:
for (auto it = values.begin(); it != values.end(); ++it )
{
auto & value = *it;
//...
}
有了这个,你就有了value 和迭代器it。使用你想使用的任何东西。
编辑:
虽然我不推荐这样做,但如果你想使用基于范围的 for 循环(是的,无论出于何种原因 :D),那么你可以这样做:
auto it = std::begin(values); //std::begin is a free function in C++11
for (auto& value : values)
{
//Use value or it - whatever you need!
//...
++it; //at the end OR make sure you do this in each iteration
}
这种方法避免了给定value 的搜索,因为value 和it 始终保持同步。
【讨论】:
std::find,如果你需要定位一个值......好的旧算法仍在新标准中。
value 和 it 可能不同步。记住value 是一个参考。
++it 而不是it++(在您的代码中都使用),因为它的开销可能较小。
std::vector 有一种非常简单的方法,如果您在此过程中调整矢量大小,它也应该有效(我不确定接受的答案是否考虑这种情况)
如果b 是你的向量,你可以这样做
for(auto &i:b){
auto iter = b.begin() + (&i-&*(b.begin()));
}
iter 将是您所需的迭代器。
这利用了C++ vectors are always contiguous这一事实。
【讨论】:
vector<T>::iterator 类型定义为T*:使用@987654328 进行检查@,然后只需使用T* iter = &i;。
这是一个代理包装类,允许您通过将隐藏的迭代器别名为您自己的变量来公开隐藏的迭代器。
#include <memory>
#include <iterator>
/* Only provides the bare minimum to support range-based for loops.
Since the internal iterator of a range-based for is inaccessible,
there is no point in more functionality here. */
template< typename iter >
struct range_iterator_reference_wrapper
: std::reference_wrapper< iter > {
iter &operator++() { return ++ this->get(); }
decltype( * std::declval< iter >() ) operator*() { return * this->get(); }
range_iterator_reference_wrapper( iter &in )
: std::reference_wrapper< iter >( in ) {}
friend bool operator!= ( range_iterator_reference_wrapper const &l,
range_iterator_reference_wrapper const &r )
{ return l.get() != r.get(); }
};
namespace unpolluted {
/* Cannot call unqualified free functions begin() and end() from
within a class with members begin() and end() without this hack. */
template< typename u >
auto b( u &c ) -> decltype( begin( c ) ) { return begin( c ); }
template< typename u >
auto e( u &c ) -> decltype( end( c ) ) { return end( c ); }
}
template< typename iter >
struct range_proxy {
range_proxy( iter &in_first, iter in_last )
: first( in_first ), last( in_last ) {}
template< typename T >
range_proxy( iter &out_first, T &in_container )
: first( out_first ),
last( unpolluted::e( in_container ) ) {
out_first = unpolluted::b( in_container );
}
range_iterator_reference_wrapper< iter > begin() const
{ return first; }
range_iterator_reference_wrapper< iter > end()
{ return last; }
iter &first;
iter last;
};
template< typename iter >
range_proxy< iter > visible_range( iter &in_first, iter in_last )
{ return range_proxy< iter >( in_first, in_last ); }
template< typename iter, typename container >
range_proxy< iter > visible_range( iter &first, container &in_container )
{ return range_proxy< iter >( first, in_container ); }
用法:
#include <vector>
#include <iostream>
std::vector< int > values{ 1, 3, 9 };
int main() {
// Either provide one iterator to see it through the whole container...
std::vector< int >::iterator i;
for ( auto &value : visible_range( i, values ) )
std::cout << "# " << i - values.begin() << " = " << ++ value << '\n';
// ... or two iterators to see the first incremented up to the second.
auto j = values.begin(), end = values.end();
for ( auto &value : visible_range( j, end ) )
std::cout << "# " << j - values.begin() << " = " << ++ value << '\n';
}
【讨论】:
我对此进行了尝试并找到了解决方案。
用法:
for(auto i : ForIterator(some_list)) {
// i is the iterator, which was returned by some_list.begin()
// might be useful for whatever reason
}
The implementation 没那么难:
template <typename T> struct Iterator {
T& list;
typedef decltype(list.begin()) I;
struct InnerIterator {
I i;
InnerIterator(I i) : i(i) {}
I operator * () { return i; }
I operator ++ () { return ++i; }
bool operator != (const InnerIterator& o) { return i != o.i; }
};
Iterator(T& list) : list(list) {}
InnerIterator begin() { return InnerIterator(list.begin()); }
InnerIterator end() { return InnerIterator(list.end()); }
};
template <typename T> Iterator<T> ForIterator(T& list) {
return Iterator<T>(list);
}
【讨论】:
boost::counting_iterator,它确实做到了这一点,并且方便地用 boost::counting_range 包装,所以你可以写:for(auto it : boost::counting_range(r.begin(), r.end()))。 :)
operator++() 应该返回一个InnerIterator,否则非常好用。
基于范围 for 循环被创建为 c++ 中的 foreach 在 java 中的对应项,它允许数组元素的轻松迭代。它旨在消除复杂结构(如迭代器)的使用,以使其变得简单。我你想要一个iterator,正如 Nawaz 所说,你必须使用普通的for 循环。
【讨论】:
for 是语法糖和减少打字量。必须取消引用迭代器会使其容易出错,尤其是与 auto 一起使用时