Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Os conceitos são um recurso da linguagem C++20 que restringe os parâmetros do modelo em tempo de compilação. Eles ajudam a evitar a instanciação incorreta do modelo, especificam os requisitos de argumento do modelo em um formato legível e fornecem erros mais sucintos do compilador relacionados ao modelo.
Considere o exemplo a seguir, que define um conceito para evitar a instanciação do modelo com um tipo que não suporta divisão:
// 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
}
Quando você passa a opção /diagnostics:caret do compilador para o Visual Studio 2022 versão 17.4 preview 4 ou posterior, o erro que o conceito dividable<char*> avaliado como false apontará diretamente para o requisito (a / b) de expressão que falhou.
Os conceitos de std iterador são definidos no namespace e são declarados <iterator> no arquivo de cabeçalho. Eles são usados nas declarações de adaptadores de intervalo, visualizações e assim por diante.
Existem seis categorias de iteradores. Eles estão diretamente relacionados às categorias de intervalos listados em Conceitos de intervalo.
Os seguintes conceitos de iterador são listados em ordem crescente de capacidade.
input_or_output_iterator está na extremidade inferior da hierarquia de capacidade e contiguous_iterator está na extremidade superior. Iteradores mais altos na hierarquia geralmente podem ser usados no lugar daqueles que são inferiores, mas não vice-versa. Por exemplo, um random_access_iterator iterador pode ser usado no lugar de um forward_iterator, mas não o contrário. Uma exceção é input_iterator, que não pode ser usado no lugar de output_iterator porque não pode escrever.
Na tabela a seguir, "Multi-pass" refere-se a se o iterador pode revisitar o mesmo elemento mais de uma vez. Por exemplo, vector::iterator é um iterador de várias passagens porque você pode fazer uma cópia do iterador, ler os elementos na coleção e, em seguida, restaurar o iterador para o valor na cópia e revisitar os mesmos elementos novamente. Se um iterador for de passagem única, você só poderá visitar os elementos da coleção uma vez.
Na tabela a seguir, "Tipos de exemplo" refere-se a coleções/iteradores que satisfazem o conceito.
| Conceito de iterador | Descrição | Direção | Leitura/gravação | Multi-passe | Tipos de exemplo |
|---|---|---|---|---|---|
input_or_output_iterator
C++20 |
A base da taxonomia do conceito iterador. | Avançar | Leitura/gravação | Não |
istream_iterator, ostream_iterator |
output_iterator
C++20 |
Especifica um iterador no qual você pode gravar. | Avançar | Escreve | Não |
ostream, inserter |
input_iterator
C++20 |
Especifica um iterador que você pode ler uma vez. | Avançar | Leitura | Não |
istream, istreambuf_iterator |
forward_iterator
C++20 |
Especifica um iterador que pode ler (e possivelmente gravar) várias vezes. | Avançar | Leitura/gravação | Sim |
vector, list |
bidirectional_iterator
C++20 |
Especifica um iterador que você pode ler e gravar para frente e para trás. | Para a frente ou para trás | Leitura/gravação | Sim |
list, set, multiset, map e multimap. |
random_access_iterator
C++20 |
Especifica um iterador que você pode ler e gravar por índice. | Para a frente ou para trás | Leitura/gravação | Sim |
vector, array, deque |
contiguous_iterator
C++20 |
Especifica um iterador cujos elementos são sequenciais na memória, têm o mesmo tamanho e podem ser acessados usando a aritmética de ponteiro. | Para a frente ou para trás | Leitura/gravação | Sim |
array, vector, string. |
Outros conceitos de iterador incluem:
| Conceito de iterador | Descrição |
|---|---|
sentinel_for
C++20 |
Especifica que um tipo é um sentinela para um tipo de iterador. |
sized_sentinel_for
C++20 |
Especifica que um iterador e seu sentinela podem ser subtraídos (usando -) para encontrar sua diferença em tempo constante. |
bidirectional_iterator
A bidirectional_iterator suporta leitura e escrita para a frente e para trás.
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>;
};
Parâmetros
I
O iterador para testar para ver se é um bidirectional_iteratorarquivo .
Observações
A bidirectional_iterator tem as capacidades de um forward_iterator, mas também pode iterar para trás.
Alguns exemplos de recipientes que podem ser usados com um bidirectional_iterator são set, multiset, map, multimap, vector, e list.
Exemplo: bidirectional_iterator
O exemplo a seguir usa o bidirectional_iterator conceito para mostrar que vector<int> tem um 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
Especifica um iterador cujos elementos são sequenciais na memória, têm o mesmo tamanho e podem ser acessados usando a aritmética de ponteiro.
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>>>;
};
Parâmetros
I
O tipo a ser testado para ver se é um contiguous_iteratorarquivo .
Observações
A contiguous_iterator pode ser acessado por aritmética de ponteiro porque os elementos são dispostos sequencialmente na memória e são do mesmo tamanho. Alguns exemplos de um contiguous_iterator são array, vector, e string.
Exemplo: contiguous_iterator
O exemplo a seguir usa o contiguous_iterator conceito para mostrar que a vector<int> tem um 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
Tem as capacidades de um input_iterator e um output_iterator. Suporta a iteração sobre uma coleção várias vezes.
template<class I>
concept forward_iterator =
input_iterator<I> &&
derived_from<ITER_CONCEPT(I), forward_iterator_tag> &&
incrementable<I> &&
sentinel_for<I, I>;
Parâmetros
I
O iterador para testar para ver se é um forward_iteratorarquivo .
Observações
A forward_iterator só pode avançar.
Alguns exemplos de recipientes que podem ser usados com um forward_iterator são vector, list, unordered_set, unordered_multiset, unordered_map, e unordered_multimap.
Exemplo: forward_iterator
O exemplo a seguir usa o forward_iterator conceito para mostrar que a vector<int> tem um 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
Um input_iterator é um iterador que você pode ler pelo menos uma vez.
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>;
Parâmetros
I
O tipo a ser testado para ver se é um input_iteratorarquivo .
Observações
Chamar begin() um input_iterator mais de uma vez resulta em comportamento indefinido. Um tipo que apenas modelos input_iterator não é multi-pass. Considere a leitura da entrada padrão (cin), por exemplo. Nesse caso, você só pode ler o elemento atual uma vez e não pode reler caracteres que já leu. Um input_iterator só lê para a frente, não para trás.
Exemplo: input_iterator
O exemplo a seguir usa o input_iterator conceito para mostrar que um istream_iterator tem um 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
An input_or_output_iterator é a base da taxonomia do conceito iterador. Ele suporta a desreferenciação e incremento de um iterador. Todos os modelos input_or_output_iteratoriteradores .
template<class I>
concept input_or_output_iterator =
requires(I i) {
{ *i } -> can-reference;
} &&
weakly_incrementable<I>;
Parâmetros
I
O tipo a ser testado para ver se é um input_or_output_iteratorarquivo .
Observações
O conceito can-reference significa que o tipo I é uma referência, um ponteiro ou um tipo que pode ser implicitamente convertido em uma referência.
Exemplo: input_or_output_iterator
O exemplo a seguir usa o input_or_output_iterator conceito para mostrar que vector<int> tem um 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
Um output_iterator é um iterador para o qual você pode escrever.
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);
};
Parâmetros
I
O tipo a ser testado para ver se é um output_iteratorarquivo .
T
O tipo de valores a serem gravados.
Observações
Um output_iterator é uma única passagem. Ou seja, ele só pode escrever no mesmo elemento uma vez.
Exemplo: output_iterator
O exemplo a seguir usa o output_iterator conceito para mostrar que vector<int> tem um 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
A random_access_iterator pode ler ou escrever por índice.
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>>;
};
Parâmetros
I
O tipo a ser testado para ver se é um random_access_iteratorarquivo .
Observações
A random_access_iterator tem as capacidades de um input_iterator, output_iterator, forward_iterator, e bidirectional_iterator.
Alguns exemplos de um random_access_iterator são vector, array, e deque.
Exemplo: random_access_iterator
O exemplo a seguir mostra que a vector<int> tem um 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
Especifica que um tipo é um sentinela para um iterador.
template<class S, class I>
concept sentinel_for =
semiregular<S> &&
input_or_output_iterator<I> &&
weakly-equality-comparable-with <S, I>;
Parâmetros
I
O tipo de iterador.
S
O tipo a testar para ver se é uma sentinela para I.
Observações
Uma sentinela é um tipo que pode ser comparado a um iterador para determinar se o iterador chegou ao fim. Este conceito determina se um tipo é uma sentinela para um dos input_or_output_iterator tipos, que inclui input_iterator, output_iterator, , forward_iterator, bidirectional_iteratorrandom_access_iterator, e contiguous_iterator.
Exemplo: sentinel_for
O exemplo a seguir usa o sentinel_for conceito para mostrar que vector<int>::iterator é uma sentinela para 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
Teste que um iterador e seu sentinela podem ser subtraídos usando - para encontrar a diferença, em tempo constante.
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>>;
};
Parâmetros
I
O tipo de iterador.
S
O tipo sentinela a testar.
Exemplo: sized_sentinel_for
O exemplo a seguir usa o sized_sentinel_for conceito para verificar se o foro vector<int> sentinela pode ser subtraído do iterador de vetores em tempo constante:
// 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
}