Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Los conceptos son una característica del lenguaje C++20 que restringe los parámetros de plantilla en tiempo de compilación. Ayudan a evitar la creación de instancias de plantilla incorrectas, especificar los requisitos de argumento de plantilla en un formulario legible y proporcionar más errores de compilador relacionados con la plantilla concisa.
Considere el ejemplo siguiente, que define un concepto para evitar la creación de instancias de la plantilla con un tipo que no admite la división:
// 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
}
Al pasar el modificador /diagnostics:caret del compilador a la versión preliminar 4 o posterior de Visual Studio 2022, la dividable<char*> versión preliminar 4 o posterior de Visual Studio 2022 apuntará directamente al requisito (a / b) de expresión que produjo un error.
Los conceptos de iterador se definen en el std espacio de nombres y se declaran en el archivo de <iterator> encabezado. Se usan en las declaraciones de adaptadores de rango, vistas, etc.
Hay seis categorías de iteradores. Están directamente relacionados con las categorías de rangos que aparecen en Conceptos de rango.
Los siguientes conceptos de iterador se enumeran en orden de aumentar la capacidad.
input_or_output_iterator se encuentra en el extremo inferior de la jerarquía de funcionalidades y contiguous_iterator se encuentra en el extremo superior. Los iteradores superiores en la jerarquía se pueden usar generalmente en lugar de los que son inferiores, pero no viceversa. Por ejemplo, se puede usar un random_access_iterator iterador en lugar de , forward_iteratorpero no en el otro lugar. Una excepción es input_iterator, que no se puede usar en lugar de output_iterator porque no se puede escribir.
En la tabla siguiente, "Multi-pass" hace referencia a si el iterador puede volver a visitar el mismo elemento más de una vez. Por ejemplo, vector::iterator es un iterador de varios pasos porque puede realizar una copia del iterador, leer los elementos de la colección y, a continuación, restaurar el iterador al valor de la copia y volver a revisar los mismos elementos de nuevo. Si un iterador es de un solo paso, solo puede visitar los elementos de la colección una vez.
En la tabla siguiente, "Tipos de ejemplo" hace referencia a colecciones o iteradores que satisfacen el concepto.
| Concepto de iterador | Descripción | Dirección | Lectura y escritura | Multi-pass | Tipos de ejemplo |
|---|---|---|---|---|---|
input_or_output_iterator
C++20 |
La base de la taxonomía del concepto de iterador. | Adelante | Lectura y escritura | No |
istream_iterator, ostream_iterator |
output_iterator
C++20 |
Especifica un iterador en el que se puede escribir. | Adelante | Escribir | No |
ostream, inserter |
input_iterator
C++20 |
Especifica un iterador que se puede leer de una vez. | Adelante | Lectura | No |
istream, istreambuf_iterator |
forward_iterator
C++20 |
Especifica un iterador que puede leer (y posiblemente escribir) varias veces. | Adelante | Lectura y escritura | sí |
vector, list |
bidirectional_iterator
C++20 |
Especifica un iterador que se puede leer y escribir tanto hacia delante como hacia atrás. | Adelante o atrás | Lectura y escritura | sí |
list, set, multiset, map y multimap. |
random_access_iterator
C++20 |
Especifica un iterador que se puede leer y escribir por índice. | Adelante o atrás | Lectura y escritura | sí |
vector, , array, deque |
contiguous_iterator
C++20 |
Especifica un iterador cuyos elementos son secuenciales en la memoria, tienen el mismo tamaño y se puede tener acceso a él mediante la aritmética de puntero. | Adelante o atrás | Lectura y escritura | sí |
array, , vector. string |
Otros conceptos de iterador incluyen:
| Concepto de iterador | Descripción |
|---|---|
sentinel_for
C++20 |
Especifica que un tipo es un sentinel para un tipo de iterador. |
sized_sentinel_for
C++20 |
Especifica que un iterador y su centinela se pueden restar (mediante -) para encontrar su diferencia en el tiempo constante. |
bidirectional_iterator
Un bidirectional_iterator admite la lectura y escritura hacia delante y hacia atrá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
Iterador que se va a probar para ver si es .bidirectional_iterator
Comentarios
tiene bidirectional_iterator las funcionalidades de , forward_iteratorpero también puede iterar hacia atrás.
Algunos ejemplos de contenedores que se pueden usar con bidirectional_iterator son set, multiset, map, multimap, vectory list.
Ejemplo: bidirectional_iterator
En el ejemplo siguiente se usa el bidirectional_iterator concepto para mostrar que vector<int> tiene un 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 un iterador cuyos elementos son secuenciales en la memoria, tienen el mismo tamaño y se puede tener acceso a él mediante la aritmética de puntero.
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
Tipo que se va a probar para ver si es .contiguous_iterator
Comentarios
Se contiguous_iterator puede acceder a un objeto aritmético de puntero porque los elementos se colocan secuencialmente en la memoria y tienen el mismo tamaño. Algunos ejemplos de contiguous_iterator son array, vectory string.
Ejemplo: contiguous_iterator
En el ejemplo siguiente se usa el contiguous_iterator concepto para mostrar que un vector<int> objeto tiene un 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
Tiene las funciones de y input_iterator .output_iterator Admite la iteración en una colección varias veces.
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
Iterador que se va a probar para ver si es .forward_iterator
Comentarios
Un forward_iterator solo puede avanzar.
Algunos ejemplos de contenedores que se pueden usar con forward_iterator son vector, list, unordered_set, unordered_multiset, unordered_mapy unordered_multimap.
Ejemplo: forward_iterator
En el ejemplo siguiente se usa el forward_iterator concepto para mostrar que un vector<int> objeto tiene un 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
Es input_iterator un iterador que se puede leer al menos una 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
Tipo que se va a probar para ver si es .input_iterator
Comentarios
Llamar a begin() en más de una input_iterator vez da como resultado un comportamiento indefinido. Tipo que solo los modelos input_iterator no son de paso múltiple. Considere la posibilidad de leer desde la entrada estándar (cin) por ejemplo. En este caso, solo puede leer el elemento actual una vez y no puede volver a leer los caracteres que ya ha leído. Un input_iterator solo lee hacia delante, no hacia atrás.
Ejemplo: input_iterator
En el ejemplo siguiente se usa el input_iterator concepto para mostrar que un istream_iterator objeto tiene un 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
Es input_or_output_iterator la base de la taxonomía del concepto de iterador. Admite la desreferenciación e incremento de un iterador. Cada iterador modela input_or_output_iterator.
template<class I>
concept input_or_output_iterator =
requires(I i) {
{ *i } -> can-reference;
} &&
weakly_incrementable<I>;
Parámetros
I
Tipo que se va a probar para ver si es .input_or_output_iterator
Comentarios
El concepto can-reference significa que el tipo I es una referencia, un puntero o un tipo que se puede convertir implícitamente en una referencia.
Ejemplo: input_or_output_iterator
En el ejemplo siguiente se usa el input_or_output_iterator concepto para mostrar que vector<int> tiene un 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
es output_iterator un iterador en el que se puede escribir.
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
Tipo que se va a probar para ver si es .output_iterator
T
Tipo de los valores que se van a escribir.
Comentarios
Es output_iterator un paso único. Es decir, solo puede escribir en el mismo elemento una vez.
Ejemplo: output_iterator
En el ejemplo siguiente se usa el output_iterator concepto para mostrar que vector<int> tiene un 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
Un random_access_iterator puede leer o escribir 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
Tipo que se va a probar para ver si es .random_access_iterator
Comentarios
tiene random_access_iterator las funciones de , input_iteratoroutput_iterator, forward_iteratory bidirectional_iterator.
Algunos ejemplos de random_access_iterator son vector, arrayy deque.
Ejemplo: random_access_iterator
En el ejemplo siguiente se muestra que un vector<int> objeto tiene :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 un tipo es un sentinel para un 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
Tipo de iterador.
S
Tipo que se va a probar para ver si es un sentinel para I.
Comentarios
Un sentinel es un tipo que se puede comparar con un iterador para determinar si el iterador ha llegado al final. Este concepto determina si un tipo es un sentinel para uno de los input_or_output_iterator tipos, que incluye input_iterator, , output_iteratorforward_iterator, bidirectional_iterator, y random_access_iteratorcontiguous_iterator.
Ejemplo: sentinel_for
En el ejemplo siguiente se usa el sentinel_for concepto para mostrar que vector<int>::iterator es un sentinel 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
Pruebe que un iterador y su centinela se pueden restar utilizando - para encontrar la diferencia, en tiempo 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
Tipo de iterador.
S
Tipo centinela que se va a probar.
Ejemplo: sized_sentinel_for
En el ejemplo siguiente se usa el sized_sentinel_for concepto para comprobar que el sentinel de un vector<int> elemento se puede restar del iterador de vectores en tiempo 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
}