Range-v3 用戶手冊

前言


C++14/17/20 的范圍庫。此代碼是 C++20 中范圍支持的基礎(chǔ)。

開發(fā)狀態(tài)

這些代碼相當(dāng)穩(wěn)定,經(jīng)過充分測試,適合隨意使用,盡管目前缺乏文檔。不做出任何有關(guān)支持或長期穩(wěn)定性的承諾。該代碼將不斷發(fā)展,而不考慮向后兼容性。

一個需要注意的例外是在 range::cpp20 命名空間中找到的任何內(nèi)容。這些組件很少改變或者(最好)永遠(yuǎn)不會改變。

安裝


該庫僅包含頭文件。您可以從 github 上的 range-v3 存儲庫獲取源代碼。要使用 Range-v3 進(jìn)行編譯,只需 #include 您想要的各個標(biāo)頭。

該發(fā)行版實(shí)際上包含三個獨(dú)立的僅頭文件庫:

  • include/concepts/... 包含概念可移植性預(yù)處理器 (CPP),它是一組用于定義和使用概念檢查的宏,無論您的編譯器是否恰好支持 C++20 概念語言功能。
  • include/meta/... 包含元庫,它是一組元編程實(shí)用程序,用于在編譯時處理類型和類型列表。
  • include/range/... 包含 Range-v3 庫,如下所述。

Range-v3 庫的物理結(jié)構(gòu)按功能組劃分在目錄中:

  • include/range/v3/actions/... 包含操作或可組合組件,它們在容器上急切地操作并返回變異容器以進(jìn)行進(jìn)一步操作。
  • include/range/v3/algorithms/... 除了熟悉的采用迭代器的重載之外,還包含所有具有接受范圍重載的 STL 算法。
  • include/range/v3/functional/... 包含許多函數(shù)式程序員熟悉的常用組件。
  • include/range/v3/iterator/... 包含許多有用的迭代器以及與迭代器相關(guān)的概念和實(shí)用程序的定義。
  • include/range/v3/numeric/... 包含與標(biāo)準(zhǔn) <numeric> 標(biāo)頭中的算法相對應(yīng)的數(shù)值算法。
  • include/range/v3/range/... 包含與范圍相關(guān)的實(shí)用程序,例如開始、結(jié)束和大小、范圍特征和概念以及容器的轉(zhuǎn)換。
  • include/range/v3/utility/... 包含各種可重用代碼。
  • include/range/v3/view/... 包含視圖或可組合組件,它們在范圍上延遲操作,并且本身返回可以使用附加視圖適配器進(jìn)行操作的范圍。

許可


這個項(xiàng)目中的大部分源代碼都是我的,并且都在 Boost Software License 下。 部分內(nèi)容取自 Alex Stepanov 的 Elements of Programming、Howard Hinnant 的 libc 以及 SGI STL。 請參閱隨附的許可證文件和 CREDITS 文件以獲取許可和確認(rèn)。

支持的編譯器

已知該代碼可在以下編譯器上運(yùn)行:

  • clang 5.0
  • GCC 6.5
  • Clang/LLVM 6 (or later) on Windows
  • MSVC VS2019, with /permissive- and either /std:c++latest, /std:c++20, or /std:c++17

快速開始


Range-v3 是一個通用庫,它通過使用范圍的工具來增強(qiáng)現(xiàn)有的標(biāo)準(zhǔn)庫。 范圍可以粗略地認(rèn)為是一對迭代器,盡管它們不需要以這種方式實(shí)現(xiàn)。 將開始/結(jié)束迭代器捆綁到單個對象中會帶來幾個好處:方便、可組合性和正確性。

方便
將單個范圍對象傳遞給算法比單獨(dú)的開始/結(jié)束迭代器更方便。比較:

std::vector<int> v{/*...*/};
std::sort( v.begin(), v.end() );

std::vector<int> v{/*...*/};
ranges::sor( v );

Range-v3 包含所有標(biāo)準(zhǔn)算法的完整實(shí)現(xiàn),并具有基于范圍的重載,以方便使用。

可組合
擁有單個范圍對象允許操作的管道。 在管道中,范圍以某種方式進(jìn)行延遲適應(yīng)或急切突變,結(jié)果立即可用于進(jìn)一步適應(yīng)或突變。 延遲適應(yīng)由視圖處理,急切突變由操作處理。

例如,下面使用視圖通過謂詞過濾容器,并使用函數(shù)轉(zhuǎn)換結(jié)果范圍。請注意,底層數(shù)據(jù)是常量,不會因視圖而改變。

std::vector<int> const vi{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
using namespace ranges;
auto rng = vi | views::remove_if([](int i) { return i % 2 == 1; })
              | views::transform([](int i) { return std::to_string(i); });
// rng == {"2","4","6","8","10"};

在上面的代碼中,rng 只是存儲對基礎(chǔ)數(shù)據(jù)以及過濾器和轉(zhuǎn)換函數(shù)的引用。在迭代 rng 之前不會完成任何工作。

相比之下,動作熱切地完成它們的工作,但它們也構(gòu)成了??紤]下面的代碼,它將一些數(shù)據(jù)讀入向量,對其進(jìn)行排序,并使其唯一。

extern std::vector<int> read_data();
using namespace ranges;
std::vector<int> vi = read_data() | actions::sort | actions::unique;

與視圖不同,管道中的每個步驟(actions::sort 和 actions::unique)都通過操作按值接受容器,就地改變它,然后返回它。

正確性
無論您使用視圖還是操作,您都是以純函數(shù)式、聲明式的方式操作數(shù)據(jù)。您很少需要為迭代器煩惱,盡管如果您需要它們,它們就在幕后。

通過以聲明性和功能性方式而不是命令性地操作,我們減少了對公開狀態(tài)操作以及分支和循環(huán)的需求。這會減少程序可以處于的狀態(tài)數(shù)量,從而減少錯誤數(shù)量。

簡而言之,如果您能找到一種方法將您的解決方案表達(dá)為數(shù)據(jù)上的函數(shù)轉(zhuǎn)換的組合,則可以通過構(gòu)造使您的代碼正確。

視圖(Views)


如上所述,范圍相對于迭代器的一大優(yōu)點(diǎn)是它們的可組合性。它們允許一種函數(shù)式編程,其中通過一系列組合器傳遞數(shù)據(jù)來操縱數(shù)據(jù)。此外,組合器可以是惰性的,僅在請求答案時才工作,并且是純函數(shù)式的,不會改變原始數(shù)據(jù)。這使得你更容易推理你的代碼。

視圖是一種輕量級包裝器,它以某種自定義方式呈現(xiàn)底層元素序列的視圖,而無需對其進(jìn)行更改或復(fù)制。視圖的創(chuàng)建和復(fù)制成本低廉,并且具有非擁有引用語義。以下是一些使用視圖的示例:

使用謂詞過濾容器并對其進(jìn)行轉(zhuǎn)換。

std::vector<int> const vi{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
using namespace ranges;
auto rng = vi | views::remove_if([](int i){return i % 2 == 1;})
              | views::transform([](int i){return std::to_string(i);});
// rng == {"2","4","6","8","10"};

生成從 1 開始的無限整數(shù)列表,對它們求平方,取前 10 個,并對它們求和:

using namespace ranges;
int sum = accumulate(views::ints(1)
                   | views::transform([](int i){return i*i;})
                   | views::take(10), 0);

使用范圍推導(dǎo)式動態(tài)生成一個序列,并用它初始化一個向量:

using namespace ranges;
auto vi = views::for_each(views::ints(1, 10), [](int i) {
        return yield_from(views::repeat_n(i, i));
    })
  | to<std::vector>();
// vi == {1,2,2,3,3,3,4,4,4,4,5,5,5,5,5,...}
視圖的常量性

從邏輯上講,視圖是迭代器的工廠,但實(shí)際上視圖通常被實(shí)現(xiàn)為狀態(tài)機(jī),狀態(tài)存儲在視圖對象本身中(以保持迭代器較小),并在視圖迭代時發(fā)生變化。因?yàn)橐晥D包含可變狀態(tài),所以許多視圖缺少 const 限定的begin()/end()。當(dāng)提供 begin()/end() 的 const 版本時,它們是真正的 const;也就是說,線程安全。

由于視圖呈現(xiàn)了與容器相同的接口,因此很容易認(rèn)為它們在常量性方面的行為與容器相似。事實(shí)并非如此。它們在常量性方面的行為類似于迭代器和指針。

視圖的常量性與基礎(chǔ)數(shù)據(jù)的常量性無關(guān)。 非常量視圖可以引用本身是常量的元素,反之亦然。 這類似于指針; int* const 是指向可變 int 的 const 指針,int const* 是指向 const int 的非常量指針。

盡可能使用非常量視圖。如果您需要線程安全,請?jiān)诰€程中使用視圖副本;不要共享。

視圖的有效性

對基礎(chǔ)范圍進(jìn)行的任何使其迭代器或標(biāo)記無效的操作也將使引用該范圍任何部分的任何視圖無效。 此外,當(dāng)范圍的底層元素發(fā)生變化時,某些視圖(例如,views::filter)會失效。 最好在任何可能改變底層范圍的操作之后重新創(chuàng)建視圖。

視圖列表

下面是 Range-v3 提供的惰性范圍組合器或視圖的列表,以及關(guān)于如何使用每個組合器的簡介。

  • views::addressof
    給定左值引用的源范圍,返回一個新視圖,該視圖是每個左值引用的 std::addressof 的結(jié)果。
  • views::adjacent_filter
    對于源范圍中的每對相鄰元素,計算指定的二元謂詞。如果謂詞計算結(jié)果為 false,則從結(jié)果范圍中刪除該對的第二個元素;否則,它被包括在內(nèi)。
  • views::adjacent_remove_if
    對于源范圍中的每對相鄰元素,計算指定的二元謂詞。 如果謂詞計算結(jié)果為 true,則從結(jié)果范圍中刪除該對的第一個元素; 否則,它被包括在內(nèi)。 始終包含源范圍中的最后一個元素。
  • views::all
    返回包含源中所有元素的范圍。對于將容器轉(zhuǎn)換為范圍很有用。
  • any_view<T>(rng)
    值類型為 T 的元素的類型擦除范圍;可以使用此值類型存儲任何范圍。
  • views::c_str
    將 \0 終止的 C 字符串(例如來自 const char*)視為范圍。
  • views::cache1
    緩存視圖中的最新元素,以便多次取消引用視圖的迭代器不會導(dǎo)致任何重新計算。 例如,這在包括 view::filter 和 view::transform 組合的適配器管道中非常有用。 views::cache1 始終是單通道。
  • views::cartesian_product
    枚舉 n 個范圍的 n 元笛卡爾積,即生成所有 n 元組 (e1, e2, ... , en),其中 e1 是第一個范圍的元素,e2 是第二個范圍的元素,依此類推。
  • views::chunk
    給定源范圍和整數(shù) N,生成一系列連續(xù)范圍,其中每個內(nèi)部范圍都有 N 個連續(xù)元素。最終范圍的元素可能少于 N 個。
  • views::common
    將源范圍轉(zhuǎn)換為公共范圍,其中結(jié)束的類型與開始的類型相同。對于調(diào)用 std:: 命名空間中的算法很有用。
  • views::concat
    給定 N 個源范圍,生成一個由所有源范圍串聯(lián)而成的結(jié)果范圍。
  • views::const_
    呈現(xiàn)源范圍的常量視圖。
  • views::counted
    給定一個迭代器 it 和一個計數(shù) n,創(chuàng)建一個從 it 開始并包含接下來的 n 個元素的范圍。
  • views::cycle
    返回?zé)o限重復(fù)源范圍的范圍。
  • views::delimit
    給定源范圍和值,返回一個新范圍,該范圍在源末尾或該值第一次出現(xiàn)時結(jié)束(以先到者為準(zhǔn))。 或者,可以使用迭代器和值調(diào)用 views::delimit,在這種情況下,它返回一個從指定位置開始到該值第一次出現(xiàn)為止的范圍。
  • views::drop
    給定源范圍和整數(shù)計數(shù),返回一個由源范圍中除第一個 count 元素之外的所有元素組成的范圍,如果元素較少,則返回空范圍。
  • views::drop_last
    給定一個源范圍和一個整數(shù)計數(shù),返回一個由源范圍中除最后一個 count 元素之外的所有元素組成的范圍,如果元素較少,則返回一個空范圍。
  • views::drop_exactly
    給定源范圍和整數(shù)計數(shù),返回一個由源范圍中除第一個計數(shù)元素之外的所有元素組成的范圍。源范圍必須至少包含那么多元素。
  • views::drop_while
    從范圍前面刪除滿足一元謂詞的元素。
  • views::empty
    使用給定值類型創(chuàng)建一個空范圍。
  • views::enumerate
    將范圍的每個元素與其索引配對。
  • views::filter
    給定源范圍和一元謂詞,過濾滿足謂詞的元素。 (對于 Boost.Range 的用戶來說,這就像過濾器適配器。)
  • views::for_each
    延遲地將一元函數(shù)應(yīng)用于返回另一個范圍(可能為空)的源范圍中的每個元素,從而展平結(jié)果。
  • views::generate
    給定一個零函數(shù),返回一個無限范圍,其元素是用該函數(shù)生成的。
  • views::generate_n
    給定一個空函數(shù)和一個計數(shù),返回一個范圍,通過調(diào)用該函數(shù)生成所需數(shù)量的元素。
  • views::chunk_by
    給定一個源范圍和一個二元謂詞,返回一個范圍范圍,其中每個范圍包含源范圍中的連續(xù)元素,并且滿足以下條件:對于該范圍中除第一個元素之外的每個元素,當(dāng)該元素和前一個元素傳遞給二元謂詞時,結(jié)果為 true。 本質(zhì)上,views::chunk_by 將連續(xù)元素與二元謂詞分組在一起。
  • views::indirect
    給定可讀值的源范圍(例如指針或迭代器),返回一個新視圖,該視圖是取消引用每個值的結(jié)果。
  • views::intersperse
    給定源范圍和值,返回一個新范圍,其中值插入到源中的連續(xù)元素之間。
  • views::ints
    生成一系列單調(diào)遞增的整數(shù)。 當(dāng)不帶參數(shù)使用時,它會生成準(zhǔn)無限范圍 [0,1,2,3...]。 也可以用下界或用下界和上限(不包括)來調(diào)用它。 Closed_ints 提供了包含版本。
  • views::iota
    views::ints 的泛化,生成任何可遞增類型的單調(diào)遞增值序列。 當(dāng)使用單個參數(shù)指定時,結(jié)果是從指定值開始的無限范圍。 對于兩個參數(shù),假定這些值表示半開范圍。
  • views::join
    給定一系列范圍,將它們連接成扁平的元素序列?;蛘撸梢灾付ㄒ诿總€源范圍之間插入的值或范圍。
  • views::keys
    給定一系列對(如 std::map),返回一個僅包含該對的第一個元素的新范圍。
  • views::linear_distribute
    在閉區(qū)間 [from, to] 中線性分布 n 個值(始終包含端點(diǎn))。如果 from == to,則返回 n 次 to,如果 n == 1,則返回 to。
  • views::move
    給定源范圍,返回一個新范圍,其中每個元素已轉(zhuǎn)換為右值引用。
  • views::partial_sum
    給定一個范圍和一個二元函數(shù),返回一個新范圍,其中第 N 個元素是將函數(shù)應(yīng)用于源范圍中的第 N 個元素和結(jié)果范圍中的第 (N-1) 個元素的結(jié)果。
  • views::remove
    給定源范圍和值,過濾掉那些不等于值的元素。
  • views::remove_if
    給定源范圍和一元謂詞,過濾掉那些不滿足謂詞的元素。 (對于 Boost.Range 的用戶來說,這就像謂詞被否定的過濾器適配器。)
  • views::repeat
    給定一個值,創(chuàng)建一個無限重復(fù)該值的范圍。
  • views::repeat_n
    給定一個值和一個計數(shù),創(chuàng)建一個范圍,該范圍是該值重復(fù)的 count 次。
  • views::replace
    給定源范圍、源值和目標(biāo)值,創(chuàng)建一個新范圍,其中所有等于源值的元素都將替換為目標(biāo)值。
  • views::replace
    給定源范圍、源值和目標(biāo)值,創(chuàng)建一個新范圍,其中所有等于源值的元素都將替換為目標(biāo)值。
  • views::replace_if
    給定源范圍、一元謂詞和目標(biāo)值,創(chuàng)建一個新范圍,其中滿足謂詞的所有元素都將替換為目標(biāo)值。
  • views::reverse
    創(chuàng)建一個以相反順序遍歷源范圍的新范圍。
  • views::sample
    返回長度范圍 size(range) 的隨機(jī)樣本。
  • views::slice
    為源范圍指定下限(包含)和上限(不包含),創(chuàng)建一個以指定偏移量開始和結(jié)束的新范圍。 開始和結(jié)束都可以是相對于前面的整數(shù),或者使用“end-2”語法相對于結(jié)尾的整數(shù)。
  • views::sliding
    給定范圍和計數(shù) n,在基礎(chǔ)范圍的前 n 個元素上放置一個窗口。 返回該窗口的內(nèi)容作為調(diào)整范圍的第一個元素,然后將窗口一次向前滑動一個元素,直到到達(dá)基礎(chǔ)范圍的末尾。
  • views::split
    給定源范圍和分隔符說明符,使用分隔符說明符將源范圍拆分為一系列范圍以查找邊界。 分隔符說明符可以是一個元素或一系列元素。 與分隔符匹配的元素將從結(jié)果范圍中排除。
  • views::split_when
    給定源范圍和分隔符說明符,使用分隔符說明符將源范圍拆分為一系列范圍以查找邊界。 分隔符說明符可以是謂詞或函數(shù)。 謂詞應(yīng)采用范圍引用類型的單個參數(shù),并當(dāng)且僅當(dāng)該元素是分隔符的一部分時返回 true。 該函數(shù)應(yīng)該接受一個迭代器和哨兵,指示當(dāng)前位置和源范圍的結(jié)尾,如果當(dāng)前位置是邊界,則返回 std::make_pair(true, iterator_past_the_delimiter) ; 否則 std::make_pair(false,ignored_iterator_value)。 與分隔符匹配的元素將從結(jié)果范圍中排除。
  • views::stride
    給定源范圍和整數(shù)步長值,返回由第 N 個元素組成的范圍(從第一個元素開始)。
  • views::tail
    給定源范圍,返回不帶第一個元素的新范圍。該范圍必須至少包含一個元素。
  • views::take
    給定源范圍和整數(shù)計數(shù),返回由源范圍中的第一個 count 元素組成的范圍,如果元素較少,則返回完整范圍。 (views::take 的結(jié)果不是 sized_range。)
  • views::take_exactly
    給定源范圍和整數(shù)計數(shù),返回由源范圍中的第一個計數(shù)元素組成的范圍。源范圍必須至少包含那么多元素。 (views::take_exactly 的結(jié)果是一個 sized_range。)
  • views::take_last
    給定源范圍和整數(shù)計數(shù),返回由源范圍中最后計數(shù)元素組成的范圍。源范圍必須是 sized_range。如果源范圍沒有至少 count 個元素,則返回完整范圍。
  • views::take_while
    給定源范圍和一元謂詞,返回一個由前面滿足謂詞的元素組成的新范圍。
  • views::tokenize
    給定源范圍和可選的子匹配說明符和 std::regex_constants::match_flag_type,返回 std::regex_token_iterator 以逐步遍歷源范圍的正則表達(dá)式子匹配。 子匹配說明符可以是普通 int、std::vector<int> 或 std::initializer_list<int>。
  • views::transform
    給定源范圍和一元函數(shù),返回一個新范圍,其中每個結(jié)果元素是將一元函數(shù)應(yīng)用于源元素的結(jié)果。
  • views::trim
    給定源雙向范圍和一元謂詞,返回一個新范圍,不包含滿足謂詞的前后元素。
  • views::unbounded
    給定一個迭代器,返回從該位置開始的無限范圍。
  • views::unique
    給定一個范圍,返回一個新范圍,其中除了第一個之外比較相等的所有連續(xù)元素都已被過濾掉。
  • views::values
    給定一個對的范圍(如 std::map),返回一個僅包含該對的第二個元素的新范圍。
  • views::zip
    給定 N 個范圍,返回一個新范圍,其中第 M 個元素是對所有 N 個范圍的第 M 個元素調(diào)用 make_tuple 的結(jié)果。
  • views::zip_with
    給定 N 個范圍和一個 N 元函數(shù),返回一個新范圍,其中第 M 個元素是對所有 N 個范圍的第 M 個元素調(diào)用該函數(shù)的結(jié)果。

動作(Actions)


動作列表

下面是 Range-v3 提供的立即范圍組合器或動作的列表,以及關(guān)于如何使用每個組合器的簡介。

  • actions::drop
    刪除源范圍的前 N 個元素。
  • actions::drop_while
    刪除源范圍中滿足一元謂詞的第一個元素。
  • actions::erase
    刪除源子范圍(范圍版本)中的所有元素或位置之后的所有元素。
  • actions::insert
    將范圍內(nèi)的所有元素插入源中的位置。
  • actions::join
    展平一系列范圍。
  • actions::push_back
    將元素附加到源的尾部。
  • actions::push_front
    將元素附加到源頭之前。
  • actions::remove_if
    從源中刪除滿足謂詞的所有元素。
  • actions::remove
    從源中刪除所有等于 value 的元素。
  • actions::reverse
    反轉(zhuǎn)容器中的所有元素。
  • actions::shuffle
    隨機(jī)排列源范圍。
  • actions::slice
    從源中刪除不屬于子范圍的所有元素。
  • actions::sort
    對源范圍進(jìn)行排序(不穩(wěn)定)。
  • actions::split
    使用分隔符(值、值序列、謂詞或返回對<bool, N> 的二元函數(shù))將范圍拆分為一系列子范圍。
  • actions::stable_sort
    對源范圍進(jìn)行排序(穩(wěn)定)。
  • actions::stride
    刪除位置與步幅不匹配的所有元素。
  • actions::take
    保留范圍的前 N 個元素,刪除其余元素。
  • actions::take_while
    保留滿足謂詞的第一個元素,刪除其余元素。
  • actions::transform
    用一元函數(shù)的結(jié)果替換源的元素。
  • actions::unique
    刪除源中比較相等的相鄰元素。如果源已排序,則刪除所有重復(fù)元素。
  • actions::unstable_remove_if
    更快(每個元素刪除具有恒定的時間復(fù)雜度),remove_if 的無序版本。需要雙向容器。

工具


使用 view_facade 創(chuàng)建一個用戶視圖
使用 view_adaptor 創(chuàng)建一個用戶視圖

view_adaptor 的細(xì)節(jié)

使用 basic_iterator 創(chuàng)建一個用戶迭代器

基于概念的檢查


Range-v3 及其未來


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容