Ini adalah pertanyaan yang cukup lama, tapi saya akan memasukkan 2 sen saya karena ada banyak jawaban, tetapi tidak ada yang menunjukkan semua metode yang mungkin dengan cara yang jelas dan ringkas (tidak yakin tentang bit ringkas, karena ini mendapat sedikit keluar dari tangan. TL; DR 😉).
Saya berasumsi bahwa OP ingin mengembalikan array yang diteruskan tanpa menyalin karena beberapa cara langsung melewati ini ke pemanggil untuk diteruskan ke fungsi lain untuk membuat kode terlihat lebih cantik.
Namun, menggunakan array seperti ini berarti membiarkannya membusuk menjadi sebuah pointer dan membuat kompiler memperlakukannya seperti sebuah array. Ini dapat menghasilkan bug halus jika Anda memasukkan dalam array seperti, dengan fungsi yang mengharapkan itu akan memiliki 5 elemen, tetapi pemanggil Anda benar-benar melewati beberapa nomor lainnya.
Ada beberapa cara Anda dapat menangani ini dengan lebih baik. Lulus std::vector
atau std::array
(tidak yakin apakah std::array
ada di sekitar 2010 saat pertanyaan diajukan). Anda kemudian dapat melewatkan objek sebagai referensi tanpa menyalin / memindahkan objek.
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;
}
Namun, jika Anda bersikeras bermain dengan array C, maka gunakan template yang akan menyimpan informasi berapa banyak item dalam array.
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;
}
Kecuali, itu terlihat jelek, dan sangat sulit dibaca. Saya sekarang menggunakan sesuatu untuk membantu dengan apa yang tidak ada pada tahun 2010, yang saya juga gunakan untuk fungsi pointer:
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;
}
Ini menggerakkan tipe yang diharapkan, menjadikan ini jauh lebih mudah dibaca. Tentu saja, menggunakan templat berlebihan jika Anda tidak akan menggunakan apa pun kecuali 5 elemen, jadi tentu saja Anda dapat membuat kode keras:
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;
}
Seperti yang saya katakan, type_t<>
trik saya tidak akan berfungsi pada saat pertanyaan ini diajukan. Yang terbaik yang bisa Anda harapkan saat itu adalah menggunakan tipe dalam sebuah struct:
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;
}
Yang mulai terlihat sangat jelek lagi, tetapi setidaknya masih lebih mudah dibaca, meskipun typename
mungkin opsional saat itu tergantung pada kompiler, menghasilkan:
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;
}
Dan tentu saja Anda bisa menentukan jenis tertentu, daripada menggunakan pembantu saya.
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;
}
Saat itu, fungsi std::begin()
- fungsi gratis dan std::end()
tidak ada, meskipun bisa dengan mudah diimplementasikan. Ini akan memungkinkan iterasi pada array dengan cara yang lebih aman karena masuk akal pada array C, tetapi bukan pointer.
Sedangkan untuk mengakses array, Anda bisa meneruskannya ke fungsi lain yang menggunakan tipe parameter yang sama, atau membuat alias untuk itu (yang tidak masuk akal karena Anda sudah memiliki yang asli dalam lingkup itu). Mengakses referensi array sama seperti mengakses array asli.
void other_function(type_t<int(&)[5]> x) { /* do something else */ }
void fn()
{
int array[5];
other_function(fillarr(array));
}
atau
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
}
Untuk meringkas, yang terbaik adalah tidak membiarkan peluruhan array menjadi pointer jika Anda berniat untuk mengulanginya. Itu hanya ide yang buruk karena membuat kompiler tidak melindungi Anda dari menembak diri sendiri dan membuat kode Anda lebih sulit dibaca. Selalu coba dan bantu kompiler membantu Anda dengan menjaga jenisnya selama mungkin kecuali Anda memiliki alasan yang sangat baik untuk tidak melakukannya.
Edit
Oh, dan untuk kelengkapannya, Anda bisa membiarkannya menurun ke sebuah pointer, tetapi ini memisahkan array dari jumlah elemen yang dimilikinya. Ini banyak dilakukan di C / C ++ dan biasanya dikurangi dengan melewatkan sejumlah elemen dalam array. Namun, kompiler tidak dapat membantu Anda jika Anda membuat kesalahan dan memberikan nilai yang salah ke sejumlah elemen.
// separate size value
int* fillarr(int* arr, size_t size)
{
for(int* it = arr; it != arr + size; ++it)
{ /* do stuff */ }
return arr;
}
Alih-alih melewati ukuran, Anda bisa melewati pointer akhir, yang akan menunjuk ke salah satu melewati ujung array Anda. Ini berguna karena membuat untuk sesuatu yang lebih dekat dengan algoritma std, yang mengambil pointer mulai dan akhir, tetapi apa yang Anda kembalikan sekarang hanya sesuatu yang harus Anda ingat.
// separate end pointer
int* fillarr(int* arr, int* end)
{
for(int* it = arr; it != end; ++it)
{ /* do stuff */ }
return arr;
}
Atau, Anda dapat mendokumentasikan bahwa fungsi ini hanya akan mengambil 5 elemen dan berharap pengguna fungsi Anda tidak melakukan hal bodoh.
// 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;
}
Perhatikan bahwa nilai kembali telah kehilangan tipe aslinya dan diturunkan ke sebuah pointer. Karena itu, Anda sekarang sendirian untuk memastikan bahwa Anda tidak akan membanjiri array.
Anda dapat melewati a std::pair<int*, int*>
, yang dapat Anda gunakan untuk memulai dan mengakhiri dan menyebarkannya, tetapi kemudian itu benar-benar berhenti terlihat seperti sebuah array.
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];
}
atau
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])));
}
Cukup lucu, ini sangat mirip dengan cara std::initializer_list
kerjanya (c ++ 11), tetapi mereka tidak bekerja dalam konteks ini.