分享方式:


<ranges> 概念

概念是C++20語言功能,可在編譯時期限制範本參數。 它們有助於防止不正確的範本具現化、以可讀取形式指定範本自變數需求,並提供更簡潔的範本相關編譯程序錯誤。

請考慮下列範例,其會定義概念,以防止具現化具有不支援除法的類型範本:

// requires /std:c++20 or later
#include <iostream>

// Definition of dividable concept which requires 
// that arguments a & b of type T support division
template <typename T>
concept dividable = requires (T a, T b)
{
    a / b;
};

// Apply the concept to a template.
// The template will only be instantiated if argument T supports division.
// This prevents the template from being instantiated with types that don't support division.
// This could have been applied to the parameter of a template function, but because
// most of the concepts in the <ranges> library are applied to classes, this form is demonstrated.
template <class T> requires dividable<T>
class DivideEmUp
{
public:
    T Divide(T x, T y)
    {
        return x / y;
    }
};

int main()
{
    DivideEmUp<int> dividerOfInts;
    std::cout << dividerOfInts.Divide(6, 3); // outputs 2
    // The following line will not compile because the template can't be instantiated 
    // with char* because char* can be divided
    DivideEmUp<char*> dividerOfCharPtrs; // compiler error: cannot deduce template arguments 
}

當您將編譯程序參數 /diagnostics:caret 傳遞至Visual Studio 2022 17.4版 Preview 4 或更新版本時,評估為 false 的概念 dividable<char*> 錯誤會直接指向失敗的表達式需求 (a / b)

範圍概念定義於 std::ranges 命名空間中,並在頭檔中宣告 <ranges> 。 它們用於範圍配接器檢視表等的宣告中。

有六種範圍。 它們與概念中列出的<iterator>反覆運算器類別相關。 為了增加功能,類別如下:

範圍概念 描述
output_range
input_range
指定您可以寫入的範圍。
指定可從一次讀取的範圍。
forward_range 指定一個範圍,您可以讀取(且可能寫入)多次。
bidirectional_range 指定您可以向前和向後讀取和寫入的範圍。
random_access_range 指定您可以依索引讀取和寫入的範圍。
contiguous_range 指定範圍,其元素在記憶體中循序、大小相同,而且可以使用指標算術來存取。

在上表中,概念會依增加功能的順序列出。 符合概念需求的範圍,通常會符合其前面數據列中之概念的需求。 例如,random_access_range具有、forward_rangeinput_rangeoutput_range的功能bidirectional_range。 例外狀況是 input_range,無法寫入 ,因此它沒有的功能 output_range

範圍反覆運算器階層的圖表。input_range和output_range是最基本的反覆運算器。forward_range是下一個,並精簡input_range和output_range。bidirectional_range精簡forward_range。random_access_range精簡bidirectional_range。最後,contiguous_range精簡random_access_range

其他範圍概念包括:

範圍概念 描述
rangeC++20 指定提供反覆運算器和 sentinel 的類型。
borrowed_rangeC++20 指定範圍的反覆運算器存留期不會繫結至範圍的存留期。
common_rangeC++20 指定範圍反覆運算器的類型和範圍的sentinel類型相同。
Simple_ViewC++20 不是定義為標準連結庫一部分的官方概念,而是用來作為某些介面的協助程式概念。
sized_rangeC++20 指定可以有效率地提供其項目數目的範圍。
viewC++20 指定具有有效(常數時間)移動建構、指派和解構的類型。
viewable_rangeC++20 指定類型,該類型為檢視或可以轉換成檢視。

bidirectional_range

bidirectional_range支援向前和向後讀取和寫入範圍。

template<class T>
concept bidirectional_range =
    forward_range<T> && bidirectional_iterator<iterator_t<T>>;

參數

T
要測試的類型,以檢視其是否為 bidirectional_range

備註

這種範圍支援 bidirectional_iterator 或更新版本。 bidirectional_iterator具有的功能forward_iterator,但也可以往後反覆運算。

bidirectional_range 些範例包括 std::setstd::vectorstd::list

borrowed_range

borrowed_range如果您從物件取得的反覆運算器有效性,類型模型可能會高於物件的存留期。 也就是說,即使範圍已不存在,也可以使用範圍的反覆運算器。

template<class T>
concept borrowed_range =
    range<T> &&
    (is_lvalue_reference_v<T> || enable_borrowed_range<remove_cvref_t<T>>);

參數

T
要測試的類型,以檢視其是否為 borrowed_range

備註

rvalue 範圍的存留期可以結束於函數調用之後,不論範圍模型是否。borrowed_range borrowed_range如果是 ,則不論範圍的存留期何時結束,您都可以繼續使用具有妥善定義行為的反覆運算器。

例如,如果vectorlist容器的存留期結束,則反覆運算器會參考已終結的元素,例如,如果容器的存留期結束,則情況並非如此。

例如,您可以繼續使用 反覆運算器作為borrowed_range的反覆運算器,例如iota_view<int>{0, 42}view其反覆運算器超過一組值,這些值不受限於被終結,因為它們是隨選產生的。

common_range

的反覆運算器 common_range 類型與 sentinel 的類型相同。 也就是說, begin()end() 傳回相同的類型。

template<class T>
concept common_range =
   ranges::range<T> && std::same_as<ranges::iterator_t<T>, ranges::sentinel_t<T>>;

參數

T
要測試的類型,以檢視其是否為 common_range

備註

std::ranges::begin() 取得 std::ranges::end() 型別,對於計算兩個反覆運算器之間的距離,以及接受反覆運算器配對所表示範圍的演算法而言很重要。

標準容器 (例如, vector) 符合的需求 common_range

contiguous_range

contiguous_range 元素會循序儲存在記憶體中,而且可以使用指標算術來存取。 例如,陣列是 contiguous_range

template<class T>
concept contiguous_range =
    random_access_range<T> && contiguous_iterator<iterator_t<T>> &&
    requires(T& t) {{ ranges::data(t) } -> same_as<add_pointer_t<range_reference_t<T>>>;};

參數

T
要測試的類型,以檢視其是否為 contiguous_range

備註

contiguous_range指標算術可以存取 ,因為元素會循序配置在記憶體中,而且大小相同。 這種範圍支援 continguous_iterator,這是所有反覆運算器中最有彈性的。

contiguous_range 些範例包括 std::arraystd::vectorstd::string

範例: contiguous_range

下列範例示範如何使用指標算術來存取 contiguous_range

// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <vector>

int main()
{
    // Show that vector is a contiguous_range
    std::vector<int> v = {0,1,2,3,4,5};
    std::cout << std::boolalpha << std::ranges::contiguous_range<decltype(v)> << '\n'; // outputs true

    // Show that pointer arithmetic can be used to access the elements of a contiguous_range
    auto ptr = v.data();
    ptr += 2;
    std::cout << *ptr << '\n'; // outputs 2
}
true
2

forward_range

forward_range支援讀取範圍數次(且可能寫入)。

template<class T>
concept forward_range = input_range<T> && forward_iterator<iterator_t<T>>;

參數

T
要測試的類型,以檢視其是否為 forward_range

備註

這種範圍支援 forward_iterator 或更新版本。 forward_iterator可以多次逐一查看範圍。

input_range

input_range是可從一次讀取的範圍。

template<class T>
concept input_range = range<T> && input_iterator<iterator_t<T>>;

參數

T
要測試的類型,以檢視其是否為 input_range

備註

當類型符合的需求 input_range時:

  • ranges::begin() 式會傳 input_iterator回 。 在上input_range呼叫begin()多次會導致未定義的行為。
  • 您可以重複取 input_iterator 值,每次產生相同的值。 input_range不是多重傳遞。 遞增反覆運算器會使任何複本失效。
  • 它可以與 搭配 ranges::for_each使用。
  • 它支援 input_iterator 或更新版本。

output_range

output_range是您可以寫入的範圍。

template<class R, class T>
concept output_range = range<R> && output_iterator<iterator_t<R>, T>;

參數

R
範圍的型別。

T
要寫入範圍的數據型別。

備註

的意義 output_iterator<iterator_t<R>, T> 在於,此類型會提供反覆運算器,可將 型 T 別的值寫入類型 R範圍。 換句話說,它支援 output_iterator 或更新版本。

random_access_range

random_access_range可以依索引讀取或寫入範圍。

template<class T>
concept random_access_range =
bidirectional_range<T> && random_access_iterator<iterator_t<T>>;

參數

T
要測試的類型,以檢視其是否為 sized_range

備註

這種範圍支援 random_access_iterator 或更新版本。 random_access_range具有、output_rangeforward_rangebidirectional_range的功能input_rangerandom_access_range是可排序的。

random_access_range 些範例包括 std::vectorstd::arraystd::deque

range

定義類型必須符合才能 range成為的需求。 range提供反覆運算器和 sentinel,讓您可以逐一查看其元素。

template<class T>
concept range = requires(T& rg)
{
  ranges::begin(rg);
  ranges::end(rg);
};

參數

T
要測試的類型,以檢視其是否為 range

備註

的需求 range 如下:

  • 它可以使用和進行 Iterated std::ranges::begin()std::ranges::end()
  • ranges::begin()ranges::end() 在分攤的常數時間中執行,且不會修改 range。 分攤常數時間並不意味著 O(1),但一系列通話的平均成本,即使在最壞的情況下,是 O(n) 而不是 O(n^2) 或更糟。
  • [ranges::begin(), ranges::end()) 表示有效的範圍。

Simple_View

Simple_View是某些介面上使用ranges的僅限說明概念。 它未定義於連結庫中。 它只會用於規格中,以協助描述某些範圍配接器的行為。

template<class V>
  concept Simple_View = // exposition only
    ranges::view<V> && ranges::range<const V> &&
    std::same_as<std::ranges::iterator_t<V>, std::ranges::iterator_t<const V>> &&
    std::same_as<std::ranges::sentinel_t<V>, std::ranges::sentinel_t<const V>>;

參數

V
要測試的類型,以檢視其是否為 Simple_View

備註

如果下列所有專案都成立,則檢視 VSimple_View

  • V 是檢視
  • const V 是範圍
  • vconst V 都有相同的反覆運算器和 sentinel 類型。

sized_range

提供 sized_range 分攤常數時間範圍中的項目數目。

template<class T>
  concept sized_range = range<T> &&
    requires(T& t) { ranges::size(t); };

參數

T
要測試的類型,以檢視其是否為 sized_range

備註

的需求 sized_range 是呼叫 ranges::size 它:

  • 不會修改範圍。
  • 傳回分攤常數時間中的項目數目。 分攤常數時間並不意味著 O(1),但一系列通話的平均成本,即使在最壞的情況下,是 O(n) 而不是 O(n^2) 或更糟。

sized_range些範例為 std::list 與 。 std::vector

範例: sized_range

下列範例顯示 vector intsized_range是 :

// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <vector>

int main()
{
    std::cout << std::boolalpha << std::ranges::sized_range<std::vector<int>> << '\n'; // outputs "true"
}    

view

具有 view 固定的時間移動建構、指派和解構作業,不論其擁有的項目數目為何。 檢視不需要複製可建構或可複製可指派,但如果是,這些作業也必須在固定時間內執行。

由於固定的時間需求,您可以有效率地撰寫檢視。 例如,假設有一個稱為inputint向量,這個函式會判斷數位是否以三除,而一個將數位平方的函式,語句auto x = input | std::views::filter(divisible_by_three) | std::views::transform(square);就會有效率地產生檢視,其中包含輸入中三個可除數的平方。 將檢視與 | 連接在一起稱為撰寫檢視。 如果類型符合 view 概念,則可以有效率地撰寫。

template<class T>
concept view = ranges::range<T> && std::movable<T> && ranges::enable_view<T>;

參數

T
要測試的類型,以查看其是否為檢視。

備註

使檢視可組合的基本需求是移動/複製成本低廉。 這是因為檢視是以另一個檢視撰寫時移動/複製。 它必須是可移動的範圍。

ranges::enable_view<T> 是用來宣告符合概念語 view 意需求的特性。 類型可以透過下列方式選擇加入:

  • 公開且明確衍生自 特製化的 ranges::view_interface
  • 公開且明確衍生自空類別 ranges::view_base、 或
  • ranges::enable_view<T>專精於true

偏好選項 1,因為 view_interface 也提供預設實作來儲存您必須撰寫的一些未定案程序代碼。

如果失敗,選項 2 比選項 3 簡單一點。

選項 3 的優點是,不需變更型別的定義即可。

viewable_range

viewable_range是一種類型,可以是檢視或可以轉換成檢視。

template<class T>
  concept viewable_range =
    range<T> && (borrowed_range<T> || view<remove_cvref_t<T>>);

參數

T
要測試的類型,以查看其是否為檢視或可轉換成檢視。

備註

使用 std::ranges::views::all() 將範圍轉換成檢視。

另請參閱

<ranges>
範圍配接器
檢視類別