迭代 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