共用方式為


<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

Diagram of the ranges iterator hierarchy. input_range and output_range are the most basic iterators. forward_range is next and refines both input_range and output_range. bidirectional_range refines forward_range. random_access_range refines bidirectional_range. Finally, contiguous_range refines 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::liststd::vector

範例: sized_range

下列範例顯示 vectorintsized_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>
範圍配接器
檢視類別