Esta es una pregunta bastante antigua, pero voy a poner mis 2 centavos, ya que hay muchas respuestas, pero ninguna muestra todos los métodos posibles de una manera clara y concisa (no estoy seguro acerca del bit conciso, ya que esto obtuvo un poco fuera de control TL; DR 😉).
Supongo que el OP quería devolver la matriz que se pasó sin copiar, ya que algunos medios de pasar esto directamente al llamador para que se pase a otra función para que el código se vea más bonito.
Sin embargo, usar una matriz como esta es dejar que se descomponga en un puntero y que el compilador la trate como una matriz. Esto puede provocar errores sutiles si pasa en una matriz como, con la función esperando que tenga 5 elementos, pero su llamador realmente pasa algún otro número.
Hay algunas maneras en que puede manejar esto mejor. Pase en unstd::vector
o std::array
(no estoy seguro si std::array
existía en 2010 cuando se hizo la pregunta). Luego puede pasar el objeto como referencia sin copiar ni mover el objeto.
std::array<int, 5>& fillarr(std::array<int, 5>& arr)
{
// (before c++11)
for(auto it = arr.begin(); it != arr.end(); ++it)
{ /* do stuff */ }
// Note the following are for c++11 and higher. They will work for all
// the other examples below except for the stuff after the Edit.
// (c++11 and up)
for(auto it = std::begin(arr); it != std::end(arr); ++it)
{ /* do stuff */ }
// range for loop (c++11 and up)
for(auto& element : arr)
{ /* do stuff */ }
return arr;
}
std::vector<int>& fillarr(std::vector<int>& arr)
{
for(auto it = arr.begin(); it != arr.end(); ++it)
{ /* do stuff */ }
return arr;
}
Sin embargo, si insiste en jugar con matrices C, utilice una plantilla que mantenga la información de cuántos elementos hay en la matriz.
template <size_t N>
int(&fillarr(int(&arr)[N]))[N]
{
// N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0])
for(int* it = arr; it != arr + N; ++it)
{ /* do stuff */ }
return arr;
}
Excepto que parece feo y súper difícil de leer. Ahora uso algo para ayudar con lo que no existía en 2010, que también uso para los punteros de función:
template <typename T>
using type_t = T;
template <size_t N>
type_t<int(&)[N]> fillarr(type_t<int(&)[N]> arr)
{
// N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0])
for(int* it = arr; it != arr + N; ++it)
{ /* do stuff */ }
return arr;
}
Esto mueve el tipo donde uno esperaría que fuera, haciendo que esto sea mucho más legible. Por supuesto, usar una plantilla es superfluo si no va a usar nada más que 5 elementos, por lo que puede, por supuesto, codificarlo:
type_t<int(&)[5]> fillarr(type_t<int(&)[5]> arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
Como dije, mi type_t<>
truco no hubiera funcionado cuando se hizo esta pregunta. Lo mejor que podría haber esperado en ese entonces era usar un tipo en una estructura:
template<typename T>
struct type
{
typedef T type;
};
typename type<int(&)[5]>::type fillarr(typename type<int(&)[5]>::type arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
Lo que comienza a verse bastante feo nuevamente, pero al menos es aún más legible, aunque typename
puede haber sido opcional en ese momento dependiendo del compilador, lo que resulta en:
type<int(&)[5]>::type fillarr(type<int(&)[5]>::type arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
Y luego, por supuesto, podría haber especificado un tipo específico, en lugar de usar mi ayudante.
typedef int(&array5)[5];
array5 fillarr(array5 arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
En aquel entonces, las funciones gratuitas std::begin()
ystd::end()
no existían, aunque podrían haberse implementado fácilmente. Esto habría permitido iterar sobre la matriz de una manera más segura ya que tienen sentido en una matriz C, pero no en un puntero.
En cuanto al acceso a la matriz, puede pasarla a otra función que tome el mismo tipo de parámetro o crear un alias (lo que no tendría mucho sentido ya que ya tiene el original en ese ámbito). Acceder a una referencia de matriz es como acceder a la matriz original.
void other_function(type_t<int(&)[5]> x) { /* do something else */ }
void fn()
{
int array[5];
other_function(fillarr(array));
}
o
void fn()
{
int array[5];
auto& array2 = fillarr(array); // alias. But why bother.
int forth_entry = array[4];
int forth_entry2 = array2[4]; // same value as forth_entry
}
Para resumir, es mejor no permitir que una matriz decaiga en un puntero si tiene la intención de iterar sobre ella. Es una mala idea, ya que evita que el compilador lo proteja de dispararse en el pie y hace que su código sea más difícil de leer. Siempre intente ayudar al compilador a ayudarlo manteniendo los tipos el mayor tiempo posible a menos que tenga una muy buena razón para no hacerlo.
Editar
Ah, y para completar, puede permitir que se degrade en un puntero, pero esto desacopla la matriz de la cantidad de elementos que contiene. Esto se hace mucho en C / C ++ y generalmente se mitiga al pasar el número de elementos en la matriz. Sin embargo, el compilador no puede ayudarlo si comete un error y pasa el valor incorrecto a la cantidad de elementos.
// separate size value
int* fillarr(int* arr, size_t size)
{
for(int* it = arr; it != arr + size; ++it)
{ /* do stuff */ }
return arr;
}
En lugar de pasar el tamaño, puede pasar el puntero final, que apuntará a uno más allá del final de su matriz. Esto es útil ya que crea algo que está más cerca de los algoritmos estándar, que toman un puntero de inicio y fin, pero lo que devuelve ahora es solo algo que debe recordar.
// separate end pointer
int* fillarr(int* arr, int* end)
{
for(int* it = arr; it != end; ++it)
{ /* do stuff */ }
return arr;
}
Alternativamente, puede documentar que esta función solo tomará 5 elementos y esperar que el usuario de su función no haga nada estúpido.
// I document that this function will ONLY take 5 elements and
// return the same array of 5 elements. If you pass in anything
// else, may nazal demons exit thine nose!
int* fillarr(int* arr)
{
for(int* it = arr; it != arr + 5; ++it)
{ /* do stuff */ }
return arr;
}
Tenga en cuenta que el valor de retorno ha perdido su tipo original y se degrada a un puntero. Debido a esto, ahora está solo para asegurarse de que no va a sobrepasar la matriz.
Podrías pasar un std::pair<int*, int*>
, que puedes usar para comenzar y terminar y pasarlo, pero luego deja de parecer una matriz.
std::pair<int*, int*> fillarr(std::pair<int*, int*> arr)
{
for(int* it = arr.first; it != arr.second; ++it)
{ /* do stuff */ }
return arr; // if you change arr, then return the original arr value.
}
void fn()
{
int array[5];
auto array2 = fillarr(std::make_pair(&array[0], &array[5]));
// Can be done, but you have the original array in scope, so why bother.
int fourth_element = array2.first[4];
}
o
void other_function(std::pair<int*, int*> array)
{
// Can be done, but you have the original array in scope, so why bother.
int fourth_element = array2.first[4];
}
void fn()
{
int array[5];
other_function(fillarr(std::make_pair(&array[0], &array[5])));
}
Curiosamente, esto es muy similar a cómo std::initializer_list
funciona (c ++ 11), pero no funcionan en este contexto.