Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Koncepty jsou funkce jazyka C++20, která omezuje parametry šablony v době kompilace. Pomáhají zabránit nesprávné instanci šablony, zadat požadavky na argument šablony ve čitelné podobě a poskytnout více stručnější chyby kompilátoru související se šablonou.
Podívejte se na následující příklad, který definuje koncept, který brání vytvoření instance šablony s typem, který nepodporuje dělení:
// 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
}
Když předáte přepínač /diagnostics:caret kompilátoru do sady Visual Studio 2022 verze 17.4 Preview 4 nebo novější, chyba, která se dividable<char*> vyhodnotí jako nepravda, bude odkazovat přímo na požadavek (a / b) výrazu, který selhal.
Koncepty iterátoru std jsou definovány v oboru názvů a deklarují se v <iterator> souboru hlaviček. Používají se v deklaraci adaptérů, zobrazení a tak dále.
Existuje šest kategorií iterátorů. Přímo souvisejí s kategoriemi rozsahů uvedených v části Koncepty rozsahů.
Následující koncepty iterátoru jsou uvedeny v pořadí zvýšení schopnosti.
input_or_output_iterator je na nízkém konci hierarchie schopností a contiguous_iterator je na nejvyšší úrovni. Iterátory vyšší v hierarchii lze obecně použít místo těch, které jsou nižší, ale ne naopak. Iterátor lze například random_access_iterator použít místo forward_iterator, ale ne naopak. Výjimkou je input_iteratorto, že místo toho nejde použít output_iterator , protože nejde napsat.
V následující tabulce "Multi-pass" odkazuje na to, zda iterátor může znovu navštívit stejný prvek více než jednou. Jedná se například o iterátor s více průchody, vector::iterator protože můžete vytvořit kopii iterátoru, přečíst prvky v kolekci a potom obnovit iterátor na hodnotu v kopii a znovu se vrátit ke stejným prvkům. Pokud je iterátor jedním průchodem, můžete prvky v kolekci navštívit jenom jednou.
V následující tabulce "Příklady typů" odkazuje na kolekce/iterátory, které vyhovují konceptu.
| Koncept iterátoru | Popis | Směr | Čtení/zápis | Vícenásobný průchod | Příklady typů |
|---|---|---|---|---|---|
input_or_output_iterator
C++20 |
Základem taxonomie konceptu iterátoru. | Dopředu | Čtení/zápis | ne |
istream_iterator, ostream_iterator |
output_iterator
C++20 |
Určuje iterátor, do kterého můžete zapisovat. | Dopředu | Napiš | ne |
ostream, inserter |
input_iterator
C++20 |
Určuje iterátor, ze kterého můžete číst jednou. | Dopředu | Čteno | ne |
istream, istreambuf_iterator |
forward_iterator
C++20 |
Určuje iterátor, který může číst (a případně zapisovat) vícekrát. | Dopředu | Čtení/zápis | ano |
vector, list |
bidirectional_iterator
C++20 |
Určuje iterátor, který můžete číst a zapisovat dopředu i dozadu. | Dopředu nebo dozadu | Čtení/zápis | ano |
list, set, multiset, map a multimap. |
random_access_iterator
C++20 |
Určuje iterátor, který můžete číst a zapisovat podle indexu. | Dopředu nebo dozadu | Čtení/zápis | ano |
vector, , arraydeque |
contiguous_iterator
C++20 |
Určuje iterátor, jehož prvky jsou sekvenční v paměti, mají stejnou velikost a lze k němu přistupovat pomocí aritmetické metody ukazatele. | Dopředu nebo dozadu | Čtení/zápis | ano |
array, , vectorstring. |
Mezi další koncepty iterátoru patří:
| Koncept iterátoru | Popis |
|---|---|
sentinel_for
C++20 |
Určuje, že typ je sentinel pro typ iterátoru. |
sized_sentinel_for
C++20 |
Určuje, že iterátor a jeho sentinel je možné odečíst (pomocí -) zjistit jejich rozdíl v konstantním čase. |
bidirectional_iterator
A bidirectional_iterator podporuje čtení a zápis dopředu a dozadu.
template<class I>
concept bidirectional_iterator =
forward_iterator<I> &&
derived_from<ITER_CONCEPT(I), bidirectional_iterator_tag> &&
requires(I i) {
{--i} -> same_as<I&>;
{i--} -> same_as<I>;
};
Parametry
I
Iterátor, který testuje, jestli je to .bidirectional_iterator
Poznámky
A bidirectional_iterator má schopnosti forward_iterator, ale může také iterovat zpět.
Některé příklady kontejnerů, které lze použít s a bidirectional_iterator jsou set, multiset, map, multimap, vector, a list.
Příklad: bidirectional_iterator
Následující příklad používá bidirectional_iterator koncept, který ukazuje, že vector<int> má bidirectional_iterator:
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
std::cout << std::boolalpha << std::bidirectional_iterator<std::vector<int>::iterator> << '\n'; // outputs "true"
// another way to test
std::vector<int> v = {0,1,2};
std::cout << std::boolalpha << std::contiguous_iterator<decltype(v)::iterator>; // outputs true
}
contiguous_iterator
Určuje iterátor, jehož prvky jsou sekvenční v paměti, mají stejnou velikost a lze k němu přistupovat pomocí aritmetické metody ukazatele.
template<class I>
concept contiguous_iterator =
random_access_iterator<I> &&
derived_from<ITER_CONCEPT(I), contiguous_iterator_tag> &&
is_lvalue_reference_v<iter_reference_t<I>> &&
same_as<iter_value_t<I>, remove_cvref_t<iter_reference_t<I>>> &&
requires(const I& i) {
{ to_address(i) } -> same_as<add_pointer_t<iter_reference_t<I>>>;
};
Parametry
I
Typ, který chcete otestovat, abyste zjistili, jestli se jedná o contiguous_iterator.
Poznámky
K Aritmetice contiguous_iterator ukazatele lze přistupovat, protože prvky jsou rozloženy postupně v paměti a mají stejnou velikost. Některé příklady contiguous_iterator jsou array, vectora string.
Příklad: contiguous_iterator
Následující příklad používá contiguous_iterator koncept, který ukazuje, že vector<int> má contiguous_iterator:
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
// Show that vector<int> has a contiguous_iterator
std::cout << std::boolalpha << std::contiguous_iterator<std::vector<int>::iterator> << '\n'; // outputs "true"
// another way to test
std::vector<int> v = {0,1,2};
std::cout << std::boolalpha << std::contiguous_iterator<decltype(v)::iterator>; // outputs true
}
forward_iterator
Má schopnosti a input_iteratoroutput_iterator. Podporuje iteraci v kolekci několikrát.
template<class I>
concept forward_iterator =
input_iterator<I> &&
derived_from<ITER_CONCEPT(I), forward_iterator_tag> &&
incrementable<I> &&
sentinel_for<I, I>;
Parametry
I
Iterátor, který testuje, jestli je to .forward_iterator
Poznámky
A forward_iterator může se pohybovat pouze vpřed.
Některé příklady kontejnerů, které lze použít s a forward_iterator jsou vector, list, unordered_set, unordered_multiset, unordered_map, a unordered_multimap.
Příklad: forward_iterator
Následující příklad používá forward_iterator koncept, který ukazuje, že vector<int> má forward_iterator:
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
// Show that vector has a forward_iterator
std::cout << std::boolalpha << std::forward_iterator<std::vector<int>::iterator> << '\n'; // outputs "true"
// another way to test
std::vector<int> v = {0,1,2};
std::cout << std::boolalpha << std::forward_iterator<decltype(v)::iterator>; // outputs true
}
input_iterator
Je input_iterator iterátor, ze kterého můžete alespoň jednou číst.
template<class I>
concept input_iterator =
input_or_output_iterator<I> &&
indirectly_readable<I> &&
requires { typename ITER_CONCEPT(I); } &&
derived_from<ITER_CONCEPT(I), input_iterator_tag>;
Parametry
I
Typ, který chcete otestovat, abyste zjistili, jestli se jedná o .input_iterator
Poznámky
Volání begin() na input_iterator více než jednou způsobí nedefinované chování. Typ, který není vícenásobný input_iterator . Zvažte například čtení ze standardního vstupu (cin). V tomto případě můžete jen jednou přečíst aktuální prvek a nemůžete číst znaky, které jste už přečetli. Jen input_iterator čtení vpřed, ne vzad.
Příklad: input_iterator
Následující příklad používá input_iterator koncept, který ukazuje, že istream_iterator má input_iterator:
// requires /std:c++20 or later
#include <iostream>
int main()
{
// Show that a istream_iterator has an input_iterator
std::cout << std::boolalpha << std::input_iterator<std::istream_iterator<int>>; // outputs true
}
input_or_output_iterator
Základem input_or_output_iterator taxonomie konceptu iterátoru je. Podporuje odvozování a zvýšení iterátoru. Každý iterátor modely input_or_output_iterator.
template<class I>
concept input_or_output_iterator =
requires(I i) {
{ *i } -> can-reference;
} &&
weakly_incrementable<I>;
Parametry
I
Typ, který chcete otestovat, abyste zjistili, jestli se jedná o .input_or_output_iterator
Poznámky
Koncept can-reference znamená, že typ I je odkaz, ukazatel nebo typ, který lze implicitně převést na odkaz.
Příklad: input_or_output_iterator
Následující příklad používá input_or_output_iterator koncept, který ukazuje, že vector<int> má input_or_output_iterator:
// requires /std:c++20 or later
#include <iostream>
int main()
{
// Show that a vector has an input_or_output_iterator
std::cout << std::boolalpha << std::input_or_output_iterator<std::vector<int>::iterator> << '\n'; // outputs true
// another way to test
std::vector<int> v = {0,1,2};
std::cout << std::boolalpha << std::input_or_output_iterator<decltype(v)::iterator>; // outputs true
}
output_iterator
Je output_iterator iterátor, na který můžete psát.
template<class I, class T>
concept output_iterator =
input_or_output_iterator<I> &&
indirectly_writable<I, T> &&
requires(I i, T&& t) {
*i++ = std::forward<T>(t);
};
Parametry
I
Typ, který chcete otestovat, abyste zjistili, jestli se jedná o .output_iterator
T
Typ hodnot, které se mají zapisovat.
Poznámky
Je output_iterator to jeden průchod. To znamená, že může zapisovat pouze do stejného prvku jednou.
Příklad: output_iterator
Následující příklad používá output_iterator koncept, který ukazuje, že vector<int> má output_iterator:
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
// Show that vector<int> has an output_iterator
std::cout << std::boolalpha << std::output_iterator<std::vector<int>::iterator, int> << "\n"; // outputs "true"
// another way to test
std::vector<int> v = {0,1,2,3,4,5};
std::cout << std::boolalpha << std::output_iterator<decltype(v)::iterator, int>; // outputs true
}
random_access_iterator
Index random_access_iterator může číst nebo zapisovat.
template<class I>
concept random_access_iterator =
bidirectional_iterator<I> &&
derived_from<ITER_CONCEPT(I), random_access_iterator_tag> &&
totally_ordered<I> &&
sized_sentinel_for<I, I> &&
requires(I i, const I j, const iter_difference_t<I> n) {
{ i += n } -> same_as<I&>;
{ j + n } -> same_as<I>;
{ n + j } -> same_as<I>;
{ i -= n } -> same_as<I&>;
{ j - n } -> same_as<I>;
{ j[n] } -> same_as<iter_reference_t<I>>;
};
Parametry
I
Typ, který chcete otestovat, abyste zjistili, jestli se jedná o random_access_iterator.
Poznámky
A random_access_iterator má schopnosti input_iterator, output_iterator, forward_iteratora bidirectional_iterator.
Některé příklady random_access_iterator jsou vector, arraya deque.
Příklad: random_access_iterator
Následující příklad ukazuje, že vector<int> má random_access_iterator:
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
// Show that vector<int> has a random_access_iterator
std::cout << std::boolalpha << std::random_access_iterator<std::vector<int>::iterator> << '\n'; // outputs "true"
// another way to test
std::vector<int> v = {0,1,2};
std::cout << std::boolalpha << std::random_access_iterator<decltype(v)::iterator>; // outputs true
}
sentinel_for
Určuje, že typ je sentinel pro iterátor.
template<class S, class I>
concept sentinel_for =
semiregular<S> &&
input_or_output_iterator<I> &&
weakly-equality-comparable-with <S, I>;
Parametry
I
Typ iterátoru.
S
Typ, který chcete otestovat, abyste zjistili, jestli se jedná o sentinel pro I.
Poznámky
Sentinel je typ, který se dá porovnat s iterátorem a určit, jestli iterátor dosáhl konce. Tento koncept určuje, zda je typ sentinelem pro jeden z input_or_output_iterator typů, který zahrnuje input_iterator, output_iterator, forward_iterator, bidirectional_iterator, random_access_iteratora contiguous_iterator.
Příklad: sentinel_for
Následující příklad používá sentinel_for koncept, který ukazuje, že vector<int>::iterator je sentinel pro vector<int>:
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = {0, 1, 2};
std::vector<int>::iterator i = v.begin();
// show that vector<int>::iterator is a sentinel for vector<int>
std::cout << std::boolalpha << std::sentinel_for<std::vector<int>::iterator, decltype(i)>; // outputs true
}
sized_sentinel_for
Otestujte, že iterátor a jeho sentinel je možné odečíst pomocí k - nalezení rozdílu v konstantním čase.
template<class S, class I>
concept sized_sentinel_for =
sentinel_for<S, I> &&
!disable_sized_sentinel_for<remove_cv_t<S>, remove_cv_t<I>> &&
requires(const I& i, const S& s) {
{s - i} -> same_as<iter_difference_t<I>>;
{i - s} -> same_as<iter_difference_t<I>>;
};
Parametry
I
Typ iterátoru.
S
Typ sentinelu, který se má testovat.
Příklad: sized_sentinel_for
Následující příklad používá sized_sentinel_for koncept k ověření, že sentinel pro a vector<int> může být odečten od iterátoru vektorů v konstantním čase:
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3 };
std::vector<int>::iterator i = v.begin();
std::vector<int>::iterator end = v.end();
// use the sized_sentinel_for concept to verify that i can be subtracted from end in constant time
std::cout << std::boolalpha << std::sized_sentinel_for<decltype(end), decltype(i)> << "\n"; // outputs true
std::cout << end - i; // outputs 3
}