簡體   English   中英

range::view::transform 產生一個 InputIterator 阻止使用 std::prev

[英]ranges::view::transform produces an InputIterator preventing the use of std::prev

考慮以下代碼,它使用 C++20 中的 Ranges 庫:

#include <vector>
#include <ranges>
#include <iostream>

int main()
{
    std::vector<int> v{0,1,2,3,4,5,6,7};

    auto transformed = std::ranges::views::transform(v, [](int i){ return i * i; });

    std::cout << *std::prev(std::end(transformed));
}

我很驚訝地得知(至少在 GCC-10.3.0 和 GCC-12.0.0 下)這段代碼卡在std::prev

發生的情況是,由於 lambda 不返回左值引用,因此transformed范圍迭代器被歸類為輸入迭代器(請參閱views::transform iterator_category選擇規則)。 但是, std::prev要求迭代器至少是一個雙向迭代器,所以我猜這段代碼實際上是 UB。 在 libstdc++ 中,將std::prev應用於輸入迭代器會導致此函數

template<typename _InputIterator, typename _Distance>
__advance(_InputIterator& __i, _Distance __n, input_iterator_tag)
{
    // concept requirements
    __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
    __glibcxx_assert(__n >= 0);
    while (__n--)
        ++__i;
}

被調用__n == -1 ,這解釋了觀察到的行為。

如果我們用手動迭代器遞減替換std::prev一切正常 切換到std::ranges::prev也有效

現在,我不能對std::vector的視圖執行std::prev顯然是荒謬的。 雖然存在一個簡單的解決方案,但我非常擔心標准庫的新舊范圍操作部分之間這種意想不到的相互作用。 所以,我的問題是:這是一個已知問題,在使用新范圍時,我真的應該忘記std::ranges命名空間中沒有的所有內容,並重寫所有現有代碼以確保它們適用於新范圍嗎?

根據 C++17 的計算,它不是隨機訪問迭代器。 transform必須返回一個值而不是一個reference ,並且 C++17 的迭代器類別不允許在 InputIterator 之上的任何東西。

但是這種類型是 C++20 規則的std::random_access_iterator ,它允許在連續以下的任何迭代器/范圍上使用類似代理的迭代器。

std::prev是 C++20 之前的工具,因此它按照 C++20 之前的規則工作。 如果您需要使用 C++20 規則,則必須使用C++20 等效項: std::ranges::prev

現在,我不能對 std::vector 的視圖執行 std::prev 顯然是荒謬的。

不,有必要 C++20 的概念化迭代器類別比以前的 C++ 版本中的迭代器類別限制更少。 這意味着有些迭代器不能在 C++20 之前的代碼中使用,而可以在 C++20 基於范圍的代碼中使用。

這就是為什么我們在ranges命名空間中為這些東西提供了新的函數。

您的轉換返回一個純右值,因此它不能InputIterator以外的任何東西。 這是 C++20 中迭代器類別已更改的主要原因之一。

如果您的操作的返回值是一個引用,那么您可以.

暫無
暫無

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

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