簡體   English   中英

使用 std::prev(vector.begin()) 或 std::next(vector.begin(), -1) 像 some_container.rend() 作為反向哨兵是否安全?

[英]Is it safe to use std::prev(vector.begin()) or std::next(vector.begin(), -1) like some_container.rend() as reversed sentry?

我寫了一些需要迭代器的代碼,但必須以相反的順序進行比較,

template<class ConstBiIter>
bool func(ConstBiIter seq_begin, ConstBiIter seq_end)
{
    ConstBiIter last = std::prev(seq_end);
    while (--last != std::prev(seq_begin)) // --> I need to compare the beginning data
    {
        ......
    }
    return true;
}

在 VS2013 中,在 Debug 模式下運行時, --last != std::prev(seq_begin)將導致調試器斷言失敗並顯示錯誤消息

Expression:string iterator + offset out of range.

但是在 Release 模式下運行並給出正確結果時完全沒問題,因為在 Released 模式下沒有邊界檢查。

我的問題是:

  1. some_container.rend()一樣使用std::prev(some_container.begin())作為哨兵some_container.rend()嗎?

  2. 如何直接將reverse_iteratoriterator進行比較? 如果我寫代碼: std::cout << (std::prev(some_container.begin())==some_container.rend()) << std::endl; 它不會編譯,即使你reinterpret_cast它們。

我很好奇prev(some_container.begin()) some_container.rend()物理上是否等於some_container.rend()

不,嘗試遞減開始迭代器是不安全的。

std::reverse_iterator (這是std::rend返回的內容)實際上並不在開始迭代器之前包含迭代器。 它存儲從概念上指向的元素到下一個元素的底層迭代器。 因此,當反向迭代器是“結束之后”(即“開始之前”)時,其底層迭代器(通過調用base() )是開始迭代器。

未定義的行為是不安全的,即使它今天在您的測試中有效。 在 C++ 中,“當我嘗試它時它有效”並不能很好地證明你做得正確:最常見的未定義行為類型之一是“它似乎有效”。

問題是未定義的行為工作從根本上來說是脆弱的。 如果你用力呼吸,它可能會破裂。

編譯器可以自由地優化僅通過未定義行為到達的分支和代碼,並且在許多情況下就是這樣做的。 在服務補丁、編譯器升級、傳遞給編譯器的標志或可執行文件路徑名的長度看似無關的更改之后,甚至可以免費這樣做。 99.9% 的時間可以免費工作,然后在其他 0.1% 的時間格式化您的硬盤。

其中一些比其他的更有可能。

雖然std::stringstd::vector元素的迭代器基本上是發布中的指針,編譯器甚至可以將指針定義為迭代器,但當下一個編譯器版本使用包裝指針時,即使這種假設也可能失敗。

C++ 標准中保留了未定義的行為,以允許編譯器編寫者自由地生成更優化的代碼。 如果你調用它,你可以踩到他們的腳趾。

也就是說,有理由使用 C++ 標准未定義的行為。 當你這樣做時,大量記錄它,隔離它,並確保回報(比如,委托速度是std::function兩倍)是值得的。

以上是非孤立的,不值得做未定義的行為,特別是因為你可以在沒有未定義的行為的情況下解決它。

如果您想向后迭代,最簡單的解決方案是制作一些反向迭代器。

template<class ConstBiIter>
bool func(ConstBiIter seq_begin, ConstBiIter seq_end)
{
  std::reverse_iterator<ConstBiIter> const rend(seq_beg);
  for (std::reverse_iterator<ConstBiIter> rit(seq_end); rit != rend; ++rit)
  {
    ......
  }
  return true;
}

現在rfirst向后迭代范圍。

如果出於某種原因需要返回引用相同元素的前向迭代器,並且您不是rend ,則可以使用std::prev(rit.base()) 如果此時rit == seq_end ,那是未定義的行為。

24.5.1 反向迭代器

類模板 reverse_iterator 是一個迭代器適配器,它從其底層迭代器定義的序列的末尾迭代到該序列的開頭。 反向迭代器與其對應的迭代器 i 之間的基本關系由以下標識建立:&*(reverse_iterator(i)) == &*(i - 1)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM