¿Cómo sabe std::end el final de una matriz?
std::begin
y std::end
conocer el principio y el final de un contenedor o una matriz.
Es fácil conocer el end
y begin
de un vector, por ejemplo, porque es una clase que proporciona esta información. Pero, ¿cómo sabe el final de un arreglo como el siguiente?
int simple_array[5]{1, 2, 3, 4, 5};
auto beg = std::begin(simple_array);
auto en = std::end(simple_array);
std::begin
No es tan difícil saber dónde comienza la matriz. ¿Pero cómo sabe dónde termina? ¿Se almacenará el número entero constante 5
en algún lugar?
Agradecería recibir una respuesta con información de bajo nivel.
Pero, ¿cómo sabe el final de una matriz?
Utiliza un parámetro de plantilla que no es de tipo para deducir el tamaño de la matriz, que luego puede usarse para producir el puntero final. La firma C++11 de la sección cppreference para std::end es la siguiente:
template< class T, std::size_t N >
T* end( T (&array)[N] );
Como señala hvd, dado que se pasa por referencia, esto evita la caída en un puntero.
La implementación sería algo similar a:
template< class T, std::size_t N >
T* end( T (&array)[N] )
{
return array + N ;
}
¿Se almacenará el número entero constante 5 en algún lugar?
5
o N
es parte del tipo de matriz y, por lo tanto, N
está disponible en el momento de la compilación. Por ejemplo, aplicar sizeof a una matriz nos dará el número total de bytes en la matriz.
Muchas veces vemos un array pasado por valor a una función. En ese caso, la matriz se descompone en un puntero al tipo almacenado en la matriz. Entonces ahora se pierde la información del tamaño. Pasar por referencia nos permite evitar esta pérdida de información y extraer el tamaño N
del tipo.
¿Se almacenará el número entero constante 5 en algún lugar?
Sí, es parte del tipo de matriz. Pero no, no está almacenado explícitamente en ningún lugar. Cuando tengas
int i[5] = { };
el tipo de i
es int[5]
. La respuesta de Shafik habla de cómo se utiliza esta longitud para implementar end
.
Si tienes C++ 11, usarlo constexpr
sería la forma más sencilla de hacerlo.
template <typename T, size_t N>
inline constexpr size_t
arrLen(const T (&arr) [N]) {
return N;
}
Si tiene un compilador anterior a C++ 11 que constexpr
no está disponible, es posible que la función anterior no se evalúe en tiempo de compilación. Entonces, en tales situaciones, puedes usar esto:
template <typename T, size_t N>
char (&arrLenFn(const T (&arr) [N]))[N];
#define arrLen(arr) sizeof(arrLenFn(arr))
Primero declaramos una función que devuelve una referencia a una matriz de N char
s, es decir, sizeof
esta función ahora sería la longitud de la matriz. Luego tenemos una macro para envolverlo, de modo que sea legible al final de la persona que llama.
Nota: Dos matrices del mismo tipo base pero con diferentes longitudes siguen siendo dos tipos completamente diferentes. int[3]
no es lo mismo que int[2]
. Sin embargo, la decadencia de la matriz le daría un resultado int*
en ambos casos. Lea ¿Cómo uso matrices en C++? si quieres saber más.
Porque le está pasando una matriz std::end
y una matriz tiene tipo T [N]
. std::end
Puedes saber cuándo termina la matriz mirando el N
tipo.