Apakah ada cara yang disukai untuk mengembalikan beberapa nilai dari fungsi C ++? Misalnya, bayangkan fungsi yang membagi dua bilangan bulat dan mengembalikan hasil bagi dan sisanya. Salah satu cara yang biasa saya lihat adalah menggunakan parameter referensi:
void divide(int dividend, int divisor, int& quotient, int& remainder);
Variasi adalah untuk mengembalikan satu nilai dan melewati yang lain melalui parameter referensi:
int divide(int dividend, int divisor, int& remainder);
Cara lain adalah dengan mendeklarasikan struct untuk berisi semua hasil dan mengembalikan bahwa:
struct divide_result {
int quotient;
int remainder;
};
divide_result divide(int dividend, int divisor);
Apakah salah satu cara ini umumnya lebih disukai, atau ada saran lain?
Sunting: Dalam kode dunia nyata, mungkin ada lebih dari dua hasil. Mereka mungkin juga dari tipe yang berbeda.
std::tuple
.std::tie
stackoverflow.com/a/2573822/502144Di C ++ 11 Anda dapat:
Di C ++ 17:
atau dengan struct:
sumber
struct
garis di luar fungsi tubuh dan gantiauto
kembali fungsi denganresult
.divide
ke dalam file cpp yang terpisah? Saya mendapatkan kesalahanerror: use of ‘auto divide(int, int)’ before deduction of ‘auto’
. Bagaimana saya mengatasi ini?Secara pribadi, saya biasanya tidak suka parameter pengembalian karena sejumlah alasan:
Saya juga memiliki beberapa keberatan tentang teknik pair / tuple. Terutama, seringkali tidak ada tatanan alami untuk nilai kembali. Bagaimana pembaca kode mengetahui apakah result.first adalah hasil bagi atau sisanya? Dan pelaksana dapat mengubah urutan, yang akan merusak kode yang ada. Ini sangat berbahaya jika nilainya adalah tipe yang sama sehingga tidak ada kesalahan kompiler atau peringatan yang akan dihasilkan. Sebenarnya, argumen ini berlaku untuk mengembalikan parameter juga.
Berikut contoh kode lain, yang ini sedikit kurang sepele:
Apakah ini mencetak kecepatan dan kursus, atau kursus dan kecepatan? Itu tidak jelas.
Bandingkan dengan ini:
Saya pikir ini lebih jelas.
Jadi saya pikir pilihan pertama saya secara umum adalah teknik struct. Gagasan pasangan / tupel kemungkinan merupakan solusi hebat dalam kasus-kasus tertentu. Saya ingin menghindari parameter pengembalian bila memungkinkan.
sumber
struct
sukaVelocity
adalah saran yang bagus. Namun, satu kekhawatiran adalah bahwa itu mencemari namespace. Saya kira bahwa dengan C ++ 11,struct
dapat memiliki nama jenis yang panjang, dan orang dapat menggunakanauto result = calculateResultingVelocity(...)
.struct { int a, b; } my_func();
. Ini dapat digunakan sebagai ini:auto result = my_func();
. Tetapi C ++ tidak mengizinkan ini: "tipe baru mungkin tidak didefinisikan dalam tipe pengembalian". Jadi saya harus membuat struct sepertistruct my_func_result_t
...auto
, jadiauto result = my_func();
sepele dapat diperoleh.std :: pair pada dasarnya adalah solusi struct Anda, tetapi sudah ditentukan untuk Anda, dan siap untuk beradaptasi dengan dua tipe data apa pun.
sumber
Ini sepenuhnya tergantung pada fungsi aktual dan arti dari beberapa nilai, dan ukurannya:
sumber
Solusi OO untuk ini adalah membuat kelas rasio. Itu tidak akan mengambil kode tambahan (akan menghemat beberapa), akan secara signifikan lebih bersih / lebih jelas, dan akan memberi Anda beberapa refactoring tambahan yang memungkinkan Anda membersihkan kode di luar kelas ini juga.
Sebenarnya saya pikir seseorang merekomendasikan untuk mengembalikan struktur, yang cukup dekat tetapi menyembunyikan maksud bahwa ini harus menjadi kelas yang dipikirkan dengan konstruktor dan beberapa metode, pada kenyataannya, "metode" yang Anda sebutkan sebelumnya (seperti mengembalikan pair) kemungkinan besar harus menjadi anggota kelas ini mengembalikan instance itu sendiri.
Saya tahu contoh Anda hanyalah "Contoh", tetapi faktanya adalah kecuali fungsi Anda melakukan lebih dari fungsi apa pun yang seharusnya dilakukan, jika Anda ingin mengembalikan beberapa nilai, Anda hampir pasti kehilangan objek.
Jangan takut untuk membuat kelas kecil ini untuk melakukan pekerjaan kecil - itulah keajaiban OO - Anda akhirnya memecahnya sampai setiap metode sangat kecil dan sederhana dan setiap kelas kecil dan dapat dimengerti.
Hal lain yang seharusnya menjadi indikator bahwa ada sesuatu yang salah: di OO Anda pada dasarnya tidak memiliki data - OO bukan tentang membagikan data, kelas perlu mengelola dan memanipulasi data itu sendiri secara internal, setiap data yang lewat (termasuk pengakses) adalah tanda bahwa Anda mungkin perlu memikirkan kembali sesuatu ..
sumber
Ada preseden untuk mengembalikan struktur dalam standar C (dan karenanya C ++) dengan fungsi
div
,ldiv
(dan, dalam C99,lldiv
) dari<stdlib.h>
(atau<cstdlib>
).'Campuran nilai pengembalian dan parameter pengembalian' biasanya yang paling bersih.
Memiliki fungsi mengembalikan status dan mengembalikan data melalui parameter pengembalian masuk akal di C; itu kurang jelas masuk akal dalam C ++ di mana Anda bisa menggunakan pengecualian untuk menyampaikan informasi kegagalan.
Jika ada lebih dari dua nilai balik, maka mekanisme seperti struktur mungkin yang terbaik.
sumber
Dengan C ++ 17 Anda juga dapat mengembalikan satu nilai bijih yang lebih tidak bergerak / tidak dapat disalin (dalam kasus tertentu). Kemungkinan untuk mengembalikan jenis yang tidak dapat digerakkan datang melalui optimisasi nilai kembali yang dijamin baru, dan menghasilkan agregat yang bagus , dan apa yang dapat disebut konstruktor templated .
Hal yang cukup tentang ini adalah bahwa hal itu dijamin untuk tidak menyebabkan setiap menyalin atau memindahkan. Anda dapat membuat contoh
many
struct variadic juga. Keterangan lebih lanjut:Mengembalikan agregat variadic (struct) dan sintaksis untuk 'C + + 17 panduan variadic template' konstruksi deduksi '
sumber
Ada banyak cara untuk mengembalikan beberapa parameter. Saya akan sangat bersemangat.
Gunakan parameter referensi:
gunakan parameter pointer:
yang memiliki keuntungan yang harus Anda lakukan
&
di situs panggilan, mungkin memperingatkan orang itu adalah parameter di luar.Tulis templat dan gunakan:
maka kita bisa melakukan:
dan semuanya baik-baik saja.
foo
tidak lagi dapat membaca nilai apa pun yang diteruskan sebagai bonus.Cara lain untuk mendefinisikan tempat Anda dapat memasukkan data dapat digunakan untuk membangun
out
. Callback untuk membereskan hal-hal di suatu tempat, misalnya.Kami dapat mengembalikan struktur:
whick berfungsi ok di setiap versi C ++, dan di c ++ 17 ini juga memungkinkan:
dengan biaya nol. Parameter bahkan tidak bisa dipindahkan berkat jaminan pemilihan.
Kami dapat mengembalikan
std::tuple
:yang memiliki kelemahan yang parameternya tidak disebutkan. Ini memungkinkanc ++ 17:
demikian juga. Sebelumc ++ 17 sebaliknya kita bisa melakukan:
yang hanya sedikit lebih canggung. Namun, penjaminan yang dijamin tidak berfungsi di sini.
Pergi ke wilayah orang asing (dan ini setelah
out<>
!), Kita dapat menggunakan gaya passing lanjutan:dan sekarang penelepon melakukan:
manfaat dari gaya ini adalah Anda dapat mengembalikan sejumlah nilai arbitrer (dengan tipe seragam) tanpa harus mengelola memori:
yang
value
callback bisa disebut ketika 500 kali Andaget_all_values( [&](int value){} )
.Untuk kegilaan murni, Anda bahkan bisa menggunakan kelanjutan pada kelanjutan.
yang penggunaannya terlihat seperti:
yang akan memungkinkan banyak-satu hubungan antara
result
danother
.Sekali lagi dengan nilai uniforn, kita dapat melakukan ini:
di sini, kami memanggil panggilan balik dengan rentang hasil. Kami bahkan dapat melakukan ini berulang kali.
Menggunakan ini, Anda dapat memiliki fungsi yang secara efisien melewati megabita data tanpa melakukan alokasi apa pun dari tumpukan.
Sekarang,
std::function
agak berat untuk ini, karena kita akan melakukan ini di lingkungan tanpa alokasi nol-overhead. Jadi kami inginfunction_view
yang tidak pernah mengalokasikan.Solusi lain adalah:
di mana alih-alih menerima panggilan balik dan menjalankannya,
foo
alih-alih mengembalikan fungsi yang menerima panggilan balik.foo (7) ([&] (hasil int, int other_result) {/ * kode * /}); ini memecah parameter output dari parameter input dengan memiliki tanda kurung terpisah.
Dengan
variant
danc ++ 20coroutine, Anda bisa membuatfoo
generator dari varian tipe kembali (atau hanya tipe kembali). Sintaksnya belum diperbaiki, jadi saya tidak akan memberikan contoh.Dalam dunia sinyal dan slot, fungsi yang memaparkan serangkaian sinyal:
memungkinkan Anda untuk membuat
foo
async yang berfungsi dan menyiarkan hasilnya ketika sudah selesai.Di bawah garis ini kami memiliki berbagai teknik pipa, di mana suatu fungsi tidak melakukan sesuatu melainkan mengatur agar data dihubungkan dengan cara tertentu, dan tindakannya relatif independen.
maka kode ini tidak melakukan apa pun sampai
int_source
memiliki bilangan bulat untuk menyediakannya. Ketika itu terjadi,int_dest1
danint_dest2
mulai menerima hasilnya.sumber
auto&&[result, other_result]=foo();
fungsi yang mengembalikan tupel dan struktur. Terima kasih!Gunakan struct atau kelas untuk nilai kembali. Menggunakan
std::pair
mungkin berfungsi untuk saat ini, tetapiMengembalikan struktur dengan nama variabel anggota yang mendokumentasikan sendiri akan cenderung lebih tidak rawan bug bagi siapa pun yang menggunakan fungsi Anda. Mengenakan topi rekan kerja saya sebentar,
divide_result
struktur Anda mudah bagi saya, pengguna potensial fungsi Anda, untuk segera mengerti setelah 2 detik. Bermain-main dengan parameter ouput atau pasangan misterius dan tupel akan membutuhkan lebih banyak waktu untuk membaca dan dapat digunakan secara tidak benar. Dan kemungkinan besar bahkan setelah menggunakan fungsi beberapa kali saya masih tidak akan ingat urutan argumen yang benar.sumber
Jika fungsi Anda mengembalikan nilai melalui referensi, kompiler tidak dapat menyimpannya dalam register saat memanggil fungsi lain karena, secara teoritis, fungsi pertama dapat menyimpan alamat variabel yang diteruskan ke variabel tersebut dalam variabel yang dapat diakses secara global, dan setiap fungsi yang dipanggil selanjutnya dapat ubahlah, sehingga kompiler akan memiliki (1) menyimpan nilai dari register kembali ke memori sebelum memanggil fungsi lain dan (2) membacanya kembali ketika diperlukan dari memori lagi setelah salah satu dari panggilan tersebut.
Jika Anda kembali dengan referensi, optimalisasi program Anda akan menderita
sumber
Di sini, saya menulis sebuah program yang mengembalikan beberapa nilai (lebih dari dua nilai) dalam c ++. Program ini dapat dieksekusi di c ++ 14 (G ++ 4.9.2). Program seperti kalkulator.
Jadi, Anda dapat dengan jelas memahami bahwa dengan cara ini Anda dapat mengembalikan beberapa nilai dari suatu fungsi. menggunakan std :: pair hanya 2 nilai yang dapat dikembalikan sementara std :: tuple dapat mengembalikan lebih dari dua nilai.
sumber
auto
tipe kembalical
untuk membuat ini lebih bersih. (IMO).Saya cenderung menggunakan out-vals dalam fungsi seperti ini, karena saya tetap berpegang pada paradigma fungsi yang mengembalikan kode sukses / kesalahan dan saya suka menjaga semuanya seragam.
sumber
Alternatif termasuk array, generator , dan inversi kontrol , tetapi tidak ada yang sesuai di sini.
Beberapa (misalnya Microsoft dalam Win32 historis) cenderung menggunakan parameter referensi untuk kesederhanaan, karena jelas siapa yang mengalokasikan dan bagaimana akan terlihat pada tumpukan, mengurangi proliferasi struktur, dan memungkinkan nilai pengembalian yang terpisah untuk sukses.
Programmer "murni" lebih suka struct, dengan asumsi itu adalah nilai fungsi (seperti halnya di sini), daripada sesuatu yang disentuh secara kebetulan oleh fungsi. Jika Anda memiliki prosedur yang lebih rumit, atau sesuatu dengan status, Anda mungkin akan menggunakan referensi (dengan asumsi Anda memiliki alasan untuk tidak menggunakan kelas).
sumber
Saya akan mengatakan tidak ada metode yang disukai, semuanya tergantung pada apa yang akan Anda lakukan dengan responsnya. Jika hasilnya akan digunakan bersama dalam proses lebih lanjut maka struktur masuk akal, jika tidak saya cenderung lulus maka sebagai referensi individu kecuali fungsi itu akan digunakan dalam pernyataan komposit:
x = divide( x, y, z ) + divide( a, b, c );
Saya sering memilih untuk membagikan 'struktur' dengan referensi dalam daftar parameter daripada meminta pass by copy overhead untuk mengembalikan struktur baru (tapi ini berkeringat pada hal-hal kecil).
void divide(int dividend, int divisor, Answer &ans)
Apakah parameter di luar membingungkan? Parameter yang dikirim sebagai referensi menunjukkan bahwa nilainya akan berubah (berlawanan dengan referensi const). Penamaan yang masuk akal juga menghilangkan kebingungan.
sumber
Mengapa Anda bersikeras pada fungsi dengan beberapa nilai pengembalian? Dengan OOP Anda dapat menggunakan kelas yang menawarkan fungsi reguler dengan nilai pengembalian tunggal, dan sejumlah "nilai pengembalian" tambahan seperti di bawah ini. Keuntungannya adalah bahwa pemanggil memiliki pilihan untuk melihat anggota data tambahan, tetapi tidak diharuskan untuk melakukan ini. Ini adalah metode yang lebih disukai untuk basis data yang rumit atau panggilan jaringan, di mana banyak info pengembalian tambahan mungkin diperlukan jika terjadi kesalahan.
Untuk menjawab pertanyaan awal Anda, contoh ini memiliki metode untuk mengembalikan hasil bagi, yang dibutuhkan oleh sebagian besar penelepon, dan selain itu, setelah panggilan metode, Anda bisa mendapatkan sisanya sebagai anggota data.
sumber
alih-alih mengembalikan beberapa nilai, kembalikan salah satunya dan buat referensi orang lain dalam fungsi yang diperlukan untuk mis:
sumber
Boost tuple akan menjadi pilihan utama saya untuk sistem umum yang mengembalikan lebih dari satu nilai dari suatu fungsi.
Contoh yang mungkin:
sumber
Kita dapat mendeklarasikan fungsi sedemikian rupa sehingga mengembalikan tipe struktur yang ditentukan pengguna atau sebuah pointer. Dan berdasarkan properti struktur, kita tahu bahwa struktur dalam C dapat menampung banyak nilai tipe asimetris (yaitu satu variabel int, empat variabel char, dua variabel float, dan seterusnya ...)
sumber
Saya hanya akan melakukannya dengan referensi jika hanya beberapa nilai pengembalian tetapi untuk tipe yang lebih kompleks Anda juga bisa melakukannya seperti ini:
gunakan "statis" untuk membatasi ruang lingkup tipe pengembalian ke unit kompilasi ini jika itu hanya dimaksudkan sebagai tipe pengembalian sementara.
Ini jelas bukan cara tercantik untuk melakukannya tetapi itu akan berhasil.
sumber
Inilah contoh lengkap dari solusi masalah semacam itu
sumber