迭代 stdvector

你可以通过多种方式迭代 std::vector 。对于以下每个部分,v 定义如下:

std::vector<int> v;

在前进方向上迭代

Version => C++ 11
// Range based for
for(const auto& value: v) {
    std::cout << value << "\n";
}

// Using a for loop with iterator
for(auto it = std::begin(v); it != std::end(v); ++it) {
    std::cout << *it << "\n";
}

// Using for_each algorithm, using a function or functor:
void fun(int const& value) {
    std::cout << value << "\n";
}

std::for_each(std::begin(v), std::end(v), fun);

// Using for_each algorithm. Using a lambda:
std::for_each(std::begin(v), std::end(v), [](int const& value) {
    std::cout << value << "\n";
});
Version < C++ 11
// Using a for loop with iterator
for(std::vector<int>::iterator it = std::begin(v); it != std::end(v); ++it) {
    std::cout << *it << "\n";
}
// Using a for loop with index
for(std::size_t i = 0; i < v.size(); ++i) {
    std::cout << v[i] << "\n";
}

在反向迭代

Version => C++ 14
// There is no standard way to use range based for for this.
// See below for alternatives.

// Using for_each algorithm
// Note: Using a lambda for clarity. But a function or functor will work
std::for_each(std::rbegin(v), std::rend(v), [](auto const& value) {
    std::cout << value << "\n";
});

// Using a for loop with iterator
for(auto rit = std::rbegin(v); rit != std::rend(v); ++rit) {
    std::cout << *rit << "\n";
}
// Using a for loop with index
for(std::size_t i = 0; i < v.size(); ++i) {
    std::cout << v[v.size() - 1 - i] << "\n";
}

虽然没有内置的方法来使用基于反向迭代的范围; 解决这个问题相对简单。基于使用 begin()end() 来获取迭代器并因此使用包装器对象模拟它的范围可以实现我们需要的结果。

Version => C++ 14
template<class C>
struct ReverseRange {
  C c; // could be a reference or a copy, if the original was a temporary
  ReverseRange(C&& cin): c(std::forward<C>(cin)) {}
  ReverseRange(ReverseRange&&)=default;
  ReverseRange& operator=(ReverseRange&&)=delete;
  auto begin() const {return std::rbegin(c);}
  auto end()   const {return std::rend(c);}
};
// C is meant to be deduced, and perfect forwarded into
template<class C>
ReverseRange<C> make_ReverseRange(C&& c) {return {std::forward<C>(c)};}

int main() {
    std::vector<int> v { 1,2,3,4};
    for(auto const& value: make_ReverseRange(v)) {
        std::cout << value << "\n";
    }
}

执行 const 元素

从 C++ 11 开始,cbegin()cend() 方法允许你为向量获取常量迭代器,即使向量是非常量的。常量迭代器允许你读取但不修改向量的内容,这有助于强制执行 const 正确性:

Version => C++ 11
// forward iteration
for (auto pos = v.cbegin(); pos != v.cend(); ++pos) {
   // type of pos is vector<T>::const_iterator
   // *pos = 5; // Compile error - can't write via const iterator
}

// reverse iteration
for (auto pos = v.crbegin(); pos != v.crend(); ++pos) {
   // type of pos is vector<T>::const_iterator
   // *pos = 5; // Compile error - can't write via const iterator
}

// expects Functor::operand()(T&) 
for_each(v.begin(), v.end(), Functor());

// expects Functor::operand()(const T&)
for_each(v.cbegin(), v.cend(), Functor())
Version => C++ 17

as_const 将此扩展到范围迭代:

for (auto const& e : std::as_const(v)) {
  std::cout << e << '\n';
}

这在早期版本的 C++中很容易实现:

Version => C++ 14
template <class T>
constexpr std::add_const_t<T>& as_const(T& t) noexcept {
  return t;
}

关于效率的一个注记

由于类 std::vector 基本上是一个管理动态分配的连续数组的类,因此这里解释的相同原理适用于 C++向量。遵循行主要顺序原则,按索引访问向量的内容要高效得多。当然,对向量的每次访问也将其管理内容也放入缓存中,但是已经多次争论(特别是在这里这里 ),与原始数组相比,迭代 std::vector 的性能差异可以忽略不计。因此,C 中原始数组的效率原理也适用于 C++的 std::vector