簡體   English   中英

視圖迭代器在視圖的生命周期之后是否有效?

[英]Are view iterators valid beyond the lifetime of the view?

假設我有一個自定義容器 class 將數據存儲在 map 中:

class Container
{
  public:
    void add(int key, std::string value) { _data.emplace(key, std::move(value)); }

  private:
    std::map<int, std::string> _data;
};

我想提供一個接口來訪問 map 的值(不是鍵)。 范圍庫提供std::views::values給我一個地圖值的范圍:

auto values() { return std::views::values(_data); }

用法:

Container c;
c.add(1, "a");
c.add(3, "b");
c.add(2, "c");

for (auto &value : c.values())
    std::cout << value << " ";  // Prints "a c b"

但是因為我想把我的 class 當作一個容器,我想要begin()end()迭代器。 我可以這樣做嗎?

auto begin() { return std::ranges::begin(values()); }
auto end() { return std::ranges::end(values()); }

在這里,我調用values()來獲取地圖值的范圍,並將迭代器獲取到范圍的開頭(或哨兵結束迭代器)。 但范圍本身超出 scope 並被破壞。 迭代器本身是否仍然有效?

這個例子中,似乎迭代器是有效的。 但是標准是否保證了這一點,無論是針對std::views:values還是針對一般視圖?

視圖迭代器在視圖的生命周期之后是否有效?

這里的屬性稱為借用范圍。 如果一個范圍是一個借用范圍,那么即使一個范圍被破壞,它的迭代器仍然有效。 R& ,如果R是一個范圍,是最簡單的借用范圍 - 因為它不是迭代器將被綁定到的引用的生命周期。 還有其他幾個熟悉的借用范圍——比如spanstring_view

有條件借用了一些范圍適配器( P2017 )。 也就是說,他們不會在他們正在適應的范圍之上添加任何額外的 state - 因此,如果基礎范圍是(或基礎范圍是),則可以借用適應的范圍。 例如,只要借用r ,就會借用views::reverse(r) 但是views::split(r, pat)不是有條件地借用的——因為模式存儲在適配器本身而不是迭代器中(假設它也可以存儲在迭代器中,但要付出代價)。

views::values(r)就是一個例子:當r被借用時,它就是一個借用范圍。 而且,在您的示例中,基礎范圍是ref_view ,它本身總是被借來的(與R&總是被借用的原理相同)。

注意這里:

auto begin() { return std::ranges::begin(values()); }
auto end() { return std::ranges::end(values()); }

將右值范圍傳遞給ranges::begin僅在借用范圍時才有效。 那是[range.access.begin]/2.1

如果E是右值並且enable_borrowed_range<remove_cv_t<T>>false ,則ranges::begin(E)格式錯誤。

所以因為你的原始代碼編譯過,你可以確定它是有效的。

C++20 標准承認范圍類型的概念,其迭代器可以比該范圍類型的特定 object 壽命更長。 這稱為“借用范圍”。 該標准定義了幾種借用范圍的類型(通過提供ranges::enable_borrowed_range的特化): subrangespanstring_view和其他幾個。

但是,沒有其他標准庫類型具有該變量的特化。 所以這些類型不是借來的范圍。

std::views::elements_view (其中values_view是其別名)不是借用范圍。 因此,您不能假設超出范圍的迭代器仍然有效。

即使它是一個借用的范圍,比較來自不同范圍的迭代器/哨兵(甚至是同一范圍的同一視圖的不同實例)也永遠無效。

暫無
暫無

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

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