Essa é uma pergunta bastante antiga, mas vou colocar meus 2 centavos, pois há muitas respostas, mas nenhuma mostrando todos os métodos possíveis de maneira clara e concisa (não tenho certeza do que é conciso, pois isso tem uma pouco fora de mão TL; DR 😉).
Estou assumindo que o OP queria retornar a matriz que foi passada sem copiar, como forma de passar isso diretamente para o chamador a ser passado para outra função para tornar o código mais bonito.
No entanto, usar uma matriz como essa é permitir que ela se decomponha em um ponteiro e faça com que o compilador a trate como uma matriz. Isso pode resultar em erros sutis se você passar em uma matriz como, com a função esperando que ela tenha 5 elementos, mas o chamador realmente passa em algum outro número.
Existem algumas maneiras de lidar melhor com isso. Passe um std::vector
ou std::array
(não tenho certeza se std::array
existia em 2010 quando a pergunta foi feita). Você pode então passar o objeto como uma referência sem copiar / mover o 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;
}
No entanto, se você insistir em jogar com matrizes C, use um modelo que manterá as informações de quantos itens na 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;
}
Exceto que parece feio e super difícil de ler. Agora uso algo para ajudar com o que não existia em 2010, que também uso para ponteiros de função:
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;
}
Isso move o tipo onde se esperaria, tornando isso muito mais legível. Obviamente, o uso de um modelo é supérfluo se você não usar nada além de cinco elementos, portanto é possível codificá-lo:
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 eu disse, meu type_t<>
truque não teria funcionado no momento em que essa pergunta foi feita. O melhor que você poderia esperar naquela época era usar um tipo em uma estrutura:
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;
}
O que começa a parecer muito feio novamente, mas pelo menos ainda é mais legível, embora typename
possa ter sido opcional na época, dependendo do compilador, resultando em:
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;
}
E então é claro que você poderia ter especificado um tipo específico, em vez de usar meu ajudante.
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;
}
Naquela época, as funções livres std::begin()
e std::end()
não existiam, embora poderiam ter sido facilmente implementadas. Isso permitiria iterar sobre a matriz de uma maneira mais segura, pois eles fazem sentido em uma matriz C, mas não em um ponteiro.
Quanto ao acesso ao array, você pode passá-lo para outra função que usa o mesmo tipo de parâmetro ou criar um alias para ele (o que não faria muito sentido, pois você já tem o original nesse escopo). Acessar uma referência de matriz é como acessar a matriz original.
void other_function(type_t<int(&)[5]> x) { /* do something else */ }
void fn()
{
int array[5];
other_function(fillarr(array));
}
ou
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, é melhor não permitir que uma matriz decaia em um ponteiro se você pretende iterar sobre ele. É apenas uma má idéia, pois impede o compilador de protegê-lo de dar um tiro no pé e torna seu código mais difícil de ler. Sempre tente ajudar o compilador, mantendo os tipos o maior tempo possível, a menos que você tenha um bom motivo para não fazê-lo.
Editar
Ah, e para garantir a integridade, você pode degradar para um ponteiro, mas isso desacopla a matriz do número de elementos que ela contém. Isso é feito muito em C / C ++ e geralmente é atenuado passando o número de elementos na matriz. No entanto, o compilador não pode ajudá-lo se você cometer um erro e passar o valor errado para o número de elementos.
// separate size value
int* fillarr(int* arr, size_t size)
{
for(int* it = arr; it != arr + size; ++it)
{ /* do stuff */ }
return arr;
}
Em vez de passar o tamanho, você pode passar o ponteiro final, que apontará para um após o final da sua matriz. Isso é útil, pois contribui para algo mais próximo dos algoritmos std, que levam um ponteiro de início e fim, mas o que você retorna agora é apenas algo que você deve lembrar.
// separate end pointer
int* fillarr(int* arr, int* end)
{
for(int* it = arr; it != end; ++it)
{ /* do stuff */ }
return arr;
}
Como alternativa, você pode documentar que essa função terá apenas 5 elementos e espere que o usuário da sua função não faça 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;
}
Observe que o valor de retorno perdeu seu tipo original e é degradado em um ponteiro. Por causa disso, agora você está por conta própria para garantir que não exceda a matriz.
Você pode passar um std::pair<int*, int*>
, que você pode usar para começar, terminar e passar por aí, mas depois ele realmente para de parecer uma 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];
}
ou
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])));
}
Engraçado o bastante, isso é muito parecido com o std::initializer_list
trabalho (c ++ 11), mas eles não funcionam nesse contexto.