<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_range
、 input_range
和 output_range
的功能bidirectional_range
。 例外狀況是 input_range
,無法寫入 ,因此它沒有的功能 output_range
。
其他範圍概念包括:
範圍概念 | 描述 |
---|---|
range C++20 |
指定提供反覆運算器和 sentinel 的類型。 |
borrowed_range C++20 |
指定範圍的反覆運算器存留期不會繫結至範圍的存留期。 |
common_range C++20 |
指定範圍反覆運算器的類型和範圍的sentinel類型相同。 |
Simple_View C++20 |
不是定義為標準連結庫一部分的官方概念,而是用來作為某些介面的協助程式概念。 |
sized_range C++20 |
指定可以有效率地提供其項目數目的範圍。 |
view C++20 |
指定具有有效(常數時間)移動建構、指派和解構的類型。 |
viewable_range C++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::set
、 std::vector
與 std::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
如果是 ,則不論範圍的存留期何時結束,您都可以繼續使用具有妥善定義行為的反覆運算器。
例如,如果vector
list
容器的存留期結束,則反覆運算器會參考已終結的元素,例如,如果容器的存留期結束,則情況並非如此。
例如,您可以繼續使用 反覆運算器作為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::array
、 std::vector
與 std::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_range
、 forward_range
和 bidirectional_range
的功能input_range
。 random_access_range
是可排序的。
一 random_access_range
些範例包括 std::vector
、 std::array
與 std::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
。
備註
如果下列所有專案都成立,則檢視 V
為 Simple_View
:
V
是檢視const V
是範圍v
和const 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
int
的 sized_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
固定的時間移動建構、指派和解構作業,不論其擁有的項目數目為何。 檢視不需要複製可建構或可複製可指派,但如果是,這些作業也必須在固定時間內執行。
由於固定的時間需求,您可以有效率地撰寫檢視。 例如,假設有一個稱為input
的int
向量,這個函式會判斷數位是否以三除,而一個將數位平方的函式,語句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()
將範圍轉換成檢視。