Mengembalikan banyak nilai dari fungsi C ++


242

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.

Jawaban:


217

Untuk mengembalikan dua nilai, saya menggunakan std::pair(biasanya mengetik). Anda harus melihat boost::tuple(dalam C ++ 11 dan yang lebih baru, ada std::tuple) untuk lebih dari dua hasil pengembalian.

Dengan pengenalan binding terstruktur dalam C ++ 17, pengembalian std::tuplemungkin harus menjadi standar yang diterima.


12
+1 untuk tuple. Perlu diingat percabangan kinerja benda-benda besar yang kembali dalam struktur vs lewat referensi.
Marcin

12
Jika Anda akan menggunakan tupel, mengapa tidak menggunakannya untuk berpasangan juga. Mengapa ada kasus khusus?
Ferruccio

4
Fred, yes boost :: tuple bisa melakukan itu :)
Johannes Schaub - litb

46
Di C ++ 11, Anda bisa menggunakan std::tuple.
Ferruccio

14
Jika Anda ingin menerima beberapa nilai dari suatu fungsi, cara mudah untuk melakukan ini adalah dengan menggunakan std::tie stackoverflow.com/a/2573822/502144
fdermishin

176

Di C ++ 11 Anda dapat:

#include <tuple>

std::tuple<int, int> divide(int dividend, int divisor) {
    return  std::make_tuple(dividend / divisor, dividend % divisor);
}

#include <iostream>

int main() {
    using namespace std;

    int quotient, remainder;

    tie(quotient, remainder) = divide(14, 3);

    cout << quotient << ',' << remainder << endl;
}

Di C ++ 17:

#include <tuple>

std::tuple<int, int> divide(int dividend, int divisor) {
    return  {dividend / divisor, dividend % divisor};
}

#include <iostream>

int main() {
    using namespace std;

    auto [quotient, remainder] = divide(14, 3);

    cout << quotient << ',' << remainder << endl;
}

atau dengan struct:

auto divide(int dividend, int divisor) {
    struct result {int quotient; int remainder;};
    return result {dividend / divisor, dividend % divisor};
}

#include <iostream>

int main() {
    using namespace std;

    auto result = divide(14, 3);

    cout << result.quotient << ',' << result.remainder << endl;

    // or

    auto [quotient, remainder] = divide(14, 3);

    cout << quotient << ',' << remainder << endl;
}

4
Saya memiliki satu masalah dengan fungsi mengembalikan tupel. Katakanlah prototipe fungsi di atas ada di header, lalu bagaimana saya tahu apa arti nilai yang dikembalikan pertama dan kedua tanpa memahami definisi fungsi? hasil bagi-sisa atau sisa-hasil.
Uchia Itachi

7
@UchiaItachi Perhatian yang sama untuk parameter fungsi, Anda dapat memberikan nama kepada mereka, tetapi bahasa bahkan tidak menegakkan itu, dan nama parameter tidak memiliki nilai di situs panggilan saat membaca. Juga, pada pengembalian tunggal, Anda hanya memiliki jenis, tetapi memiliki nama juga bisa berguna, dengan tuple Anda hanya menggandakan masalah, jadi imo, bahasa hanya kurang tentang didokumentasikan sendiri dalam beberapa cara, tidak hanya ini.
pepper_chico

1
bagaimana contoh terakhir terlihat jika saya ingin secara spesifik menentukan tipe pengembalian divide ()? Haruskah saya mendefinisikan hasil di tempat lain, atau saya bisa mendefinisikannya dengan benar di tipe spesifikasi kembali?
Slava

1
@Slava Anda tidak dapat menentukan tipe tepat di tanda tangan fungsi, Anda harus mendeklarasikan tipe di luar dan menggunakannya sebagai tipe pengembalian, seperti yang biasa dilakukan (cukup pindahkan structgaris di luar fungsi tubuh dan ganti autokembali fungsi dengan result.
pepper_chico

3
@pepper_chico Bagaimana jika ingin memasukkan definisi fungsi divideke dalam file cpp yang terpisah? Saya mendapatkan kesalahan error: use of ‘auto divide(int, int)’ before deduction of ‘auto’. Bagaimana saya mengatasi ini?
Adriaan

123

Secara pribadi, saya biasanya tidak suka parameter pengembalian karena sejumlah alasan:

  • tidak selalu jelas dalam doa parameter mana yang masuk dan yang keluar
  • Anda biasanya harus membuat variabel lokal untuk menangkap hasilnya, sementara nilai kembali dapat digunakan sebaris (yang mungkin atau mungkin bukan ide yang baik, tetapi setidaknya Anda memiliki pilihan)
  • tampaknya lebih bersih bagi saya untuk memiliki "pintu" dan "pintu keluar" ke suatu fungsi - semua input masuk ke sini, semua output keluar di sana
  • Saya suka menyimpan daftar argumen saya sesingkat mungkin

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:

pair<double,double> calculateResultingVelocity(double windSpeed, double windAzimuth,
                                               double planeAirspeed, double planeCourse);

pair<double,double> result = calculateResultingVelocity(25, 320, 280, 90);
cout << result.first << endl;
cout << result.second << endl;

Apakah ini mencetak kecepatan dan kursus, atau kursus dan kecepatan? Itu tidak jelas.

Bandingkan dengan ini:

struct Velocity {
    double speed;
    double azimuth;
};
Velocity calculateResultingVelocity(double windSpeed, double windAzimuth,
                                    double planeAirspeed, double planeCourse);

Velocity result = calculateResultingVelocity(25, 320, 280, 90);
cout << result.speed << endl;
cout << result.azimuth << endl;

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.


1
Saran untuk menyatakan structsuka Velocityadalah saran yang bagus. Namun, satu kekhawatiran adalah bahwa itu mencemari namespace. Saya kira bahwa dengan C ++ 11, structdapat memiliki nama jenis yang panjang, dan orang dapat menggunakan auto result = calculateResultingVelocity(...).
Hugues

5
+1. Suatu fungsi harus mengembalikan satu "hal", bukan "tuple of things" yang entah bagaimana dipesan.
DevSolar

1
Saya lebih suka struct daripada std :: pairs / std :: tuple untuk alasan yang dijelaskan dalam jawaban ini. Tapi saya tidak suka namespace "polusi" juga. Solusi ideal bagi saya adalah mengembalikan struct anonim 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 seperti struct my_func_result_t...
anton_rh

2
@anton_rh: C ++ 14 memungkinkan pengembalian tipe lokal dengan auto, jadi auto result = my_func();sepele dapat diperoleh.
ildjarn

4
Sekitar 15 tahun yang lalu ketika kami menemukan boost, kami menggunakan tuple banyak karena cukup berguna. Lembur kami mengalami kelemahan dalam keterbacaan terutama untuk tupel yang memiliki jenis yang sama (misalnya tupel <ganda, ganda>; mana yang mana). Jadi akhir-akhir ini kita lebih terbiasa memperkenalkan struktur POD kecil di mana setidaknya nama variabel anggota menunjukkan sesuatu yang masuk akal.
gast128

24
std::pair<int, int> divide(int dividend, int divisor)
{
   // :
   return std::make_pair(quotient, remainder);
}

std::pair<int, int> answer = divide(5,2);
 // answer.first == quotient
 // answer.second == remainder

std :: pair pada dasarnya adalah solusi struct Anda, tetapi sudah ditentukan untuk Anda, dan siap untuk beradaptasi dengan dua tipe data apa pun.


3
Itu akan bekerja untuk contoh sederhana saya. Namun secara umum, mungkin ada lebih dari dua nilai yang dikembalikan.
Fred Larson

5
Juga tidak mendokumentasikan diri. Bisakah Anda ingat register x86 mana yang merupakan sisa untuk DIV?
Tandai

1
@ Mark - Saya setuju bahwa solusi posisi bisa kurang dipelihara. Anda bisa mengalami masalah "permute and baffle".
Fred Larson

16

Ini sepenuhnya tergantung pada fungsi aktual dan arti dari beberapa nilai, dan ukurannya:

  • Jika mereka terkait seperti dalam contoh fraksi Anda, maka saya akan pergi dengan contoh struct atau kelas.
  • Jika mereka tidak benar-benar terkait dan tidak dapat dikelompokkan ke dalam kelas / struct maka mungkin Anda harus memperbaiki metode Anda menjadi dua.
  • Bergantung pada ukuran dalam memori dari nilai yang Anda kembalikan, Anda mungkin ingin mengembalikan pointer ke instance kelas atau struct, atau menggunakan parameter referensi.

1
Saya suka jawaban Anda dan peluru terakhir Anda mengingatkan saya pada sesuatu yang baru saja saya baca bahwa nilai yang lewat telah menjadi jauh lebih cepat tergantung pada keadaan yang membuat ini lebih rumit ... cpp-next.com/archive/2009/08/want-speed-pass -by-value
sage

12

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 ..


10

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.


10

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 .

template<typename T1,typename T2,typename T3>
struct many {
  T1 a;
  T2 b;
  T3 c;
};

// guide:
template<class T1, class T2, class T3>
many(T1, T2, T3) -> many<T1, T2, T3>;

auto f(){ return many{string(),5.7, unmovable()}; }; 

int main(){
   // in place construct x,y,z with a string, 5.7 and unmovable.
   auto [x,y,z] = f();
}

Hal yang cukup tentang ini adalah bahwa hal itu dijamin untuk tidak menyebabkan setiap menyalin atau memindahkan. Anda dapat membuat contoh manystruct variadic juga. Keterangan lebih lanjut:

Mengembalikan agregat variadic (struct) dan sintaksis untuk 'C + + 17 panduan variadic template' konstruksi deduksi '


6

Ada banyak cara untuk mengembalikan beberapa parameter. Saya akan sangat bersemangat.

Gunakan parameter referensi:

void foo( int& result, int& other_result );

gunakan parameter pointer:

void foo( int* result, int* other_result );

yang memiliki keuntungan yang harus Anda lakukan &di situs panggilan, mungkin memperingatkan orang itu adalah parameter di luar.

Tulis templat dan gunakan:

template<class T>
struct out {
  std::function<void(T)> target;
  out(T* t):target([t](T&& in){ if (t) *t = std::move(in); }) {}
  out(std::optional<T>* t):target([t](T&& in){ if (t) t->emplace(std::move(in)); }) {}
  out(std::aligned_storage_t<sizeof(T), alignof(T)>* t):
    target([t](T&& in){ ::new( (void*)t ) T(std::move(in)); } ) {}
  template<class...Args> // TODO: SFINAE enable_if test
  void emplace(Args&&...args) {
    target( T(std::forward<Args>(args)...) );
  }
  template<class X> // TODO: SFINAE enable_if test
  void operator=(X&&x){ emplace(std::forward<X>(x)); }
  template<class...Args> // TODO: SFINAE enable_if test
  void operator()(Args...&&args){ emplace(std::forward<Args>(args)...); }
};

maka kita bisa melakukan:

void foo( out<int> result, out<int> other_result )

dan semuanya baik-baik saja. footidak 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:

struct foo_r { int result; int other_result; };
foo_r foo();

whick berfungsi ok di setiap versi C ++, dan di ini juga memungkinkan:

auto&&[result, other_result]=foo();

dengan biaya nol. Parameter bahkan tidak bisa dipindahkan berkat jaminan pemilihan.

Kami dapat mengembalikan std::tuple:

std::tuple<int, int> foo();

yang memiliki kelemahan yang parameternya tidak disebutkan. Ini memungkinkan:

auto&&[result, other_result]=foo();

demikian juga. Sebelum sebaliknya kita bisa melakukan:

int result, other_result;
std::tie(result, other_result) = foo();

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:

void foo( std::function<void(int result, int other_result)> );

dan sekarang penelepon melakukan:

foo( [&](int result, int other_result) {
  /* code */
} );

manfaat dari gaya ini adalah Anda dapat mengembalikan sejumlah nilai arbitrer (dengan tipe seragam) tanpa harus mengelola memori:

void get_all_values( std::function<void(int)> value )

yang valuecallback bisa disebut ketika 500 kali Anda get_all_values( [&](int value){} ).

Untuk kegilaan murni, Anda bahkan bisa menggunakan kelanjutan pada kelanjutan.

void foo( std::function<void(int, std::function<void(int)>)> result );

yang penggunaannya terlihat seperti:

foo( [&](int result, auto&& other){ other([&](int other){
  /* code */
}) });

yang akan memungkinkan banyak-satu hubungan antara resultdan other.

Sekali lagi dengan nilai uniforn, kita dapat melakukan ini:

void foo( std::function< void(span<int>) > results )

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.

void foo( std::function< void(span<int>) > results ) {
  int local_buffer[1024];
  std::size_t used = 0;
  auto send_data=[&]{
    if (!used) return;
    results({ local_buffer, used });
    used = 0;
  };
  auto add_datum=[&](int x){
    local_buffer[used] = x;
    ++used;
    if (used == 1024) send_data();
  };
  auto add_data=[&](gsl::span<int const> xs) {
    for (auto x:xs) add_datum(x);
  };
  for (int i = 0; i < 7+(1<<20); ++i) {
    add_datum(i);
  }
  send_data(); // any leftover
}

Sekarang, std::functionagak berat untuk ini, karena kita akan melakukan ini di lingkungan tanpa alokasi nol-overhead. Jadi kami ingin function_viewyang tidak pernah mengalokasikan.

Solusi lain adalah:

std::function<void(std::function<void(int result, int other_result)>)> foo(int input);

di mana alih-alih menerima panggilan balik dan menjalankannya, fooalih-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 variantdancoroutine, Anda bisa membuat foogenerator 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:

template<class...Args>
struct broadcaster;

broadcaster<int, int> foo();

memungkinkan Anda untuk membuat fooasync 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.

foo( int_source )( int_dest1, int_dest2 );

maka kode ini tidak melakukan apa pun sampai int_sourcememiliki bilangan bulat untuk menyediakannya. Ketika itu terjadi, int_dest1dan int_dest2mulai menerima hasilnya.


Jawaban ini mengandung lebih banyak informasi daripada jawaban lain! khususnya, informasi tentang auto&&[result, other_result]=foo();fungsi yang mengembalikan tupel dan struktur. Terima kasih!
jjmontes

Saya menghargai jawaban lengkap ini, terutama karena saya masih terjebak dengan C ++ 11 dan karena itu tidak dapat menggunakan beberapa solusi yang lebih modern yang diusulkan orang lain.
GuyGizmo

5

Gunakan struct atau kelas untuk nilai kembali. Menggunakan std::pairmungkin berfungsi untuk saat ini, tetapi

  1. itu tidak fleksibel jika Anda memutuskan nanti Anda ingin lebih banyak info dikembalikan;
  2. tidak begitu jelas dari deklarasi fungsi di header apa yang dikembalikan dan dalam urutan apa.

Mengembalikan 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_resultstruktur 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.


4

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


4

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.

#  include <tuple>
# include <iostream>

using namespace std; 

tuple < int,int,int,int,int >   cal(int n1, int n2)
{
    return  make_tuple(n1/n2,n1%n2,n1+n2,n1-n2,n1*n2);
}

int main()
{
    int qut,rer,add,sub,mul,a,b;
    cin>>a>>b;
    tie(qut,rer,add,sub,mul)=cal(a,b);
    cout << "quotient= "<<qut<<endl;
    cout << "remainder= "<<rer<<endl;
    cout << "addition= "<<add<<endl;
    cout << "subtraction= "<<sub<<endl;
    cout << "multiplication= "<<mul<<endl;
    return 0;
}

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.


4
Dengan C ++ 14 Anda juga dapat menggunakan autotipe kembali caluntuk membuat ini lebih bersih. (IMO).
sfjac

3

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.


2

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).


2

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.


1
Saya pikir ini agak membingungkan. Seseorang yang membaca kode yang menyebutnya melihat "bagi (a, b, c);". Tidak ada indikasi bahwa c adalah outval sampai mereka mencari tanda tangan. Tapi itu adalah ketakutan umum dari par-referensi referensi non-const, daripada khusus untuk pertanyaan ini.
Steve Jessop

2

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.

class div{
   public:
      int remainder;

      int quotient(int dividend, int divisor){
         remainder = ...;
         return ...;
      }
};

1
Saya pikir ada kasus di mana ini tidak efisien. Misalnya Anda memiliki satu untuk loop yang menghasilkan beberapa nilai kembali. Jika Anda membagi nilai-nilai itu menjadi fungsi yang terpisah, Anda harus menjalankan loop sekali untuk setiap nilai.
jiggunjer

1
@ jiggunjer Anda bisa menjalankan loop sekali dan menyimpan beberapa nilai kembali dalam anggota data kelas yang terpisah. Ini menggarisbawahi fleksibilitas konsep OOP.
Roland

2

alih-alih mengembalikan beberapa nilai, kembalikan salah satunya dan buat referensi orang lain dalam fungsi yang diperlukan untuk mis:

int divide(int a,int b,int quo,int &rem)

Apakah saya tidak menyebutkan ini dalam pertanyaan itu sendiri? Juga, lihat keberatan saya dalam jawaban saya .
Fred Larson

1

Boost tuple akan menjadi pilihan utama saya untuk sistem umum yang mengembalikan lebih dari satu nilai dari suatu fungsi.

Contoh yang mungkin:

include "boost/tuple/tuple.hpp"

tuple <int,int> divide( int dividend,int divisor ) 

{
  return make_tuple(dividend / divisor,dividend % divisor )
}

1

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 ...)


1

Saya hanya akan melakukannya dengan referensi jika hanya beberapa nilai pengembalian tetapi untuk tipe yang lebih kompleks Anda juga bisa melakukannya seperti ini:

static struct SomeReturnType {int a,b,c; string str;} SomeFunction()
{
  return {1,2,3,string("hello world")}; // make sure you return values in the right order!
}

gunakan "statis" untuk membatasi ruang lingkup tipe pengembalian ke unit kompilasi ini jika itu hanya dimaksudkan sebagai tipe pengembalian sementara.

 SomeReturnType st = SomeFunction();
 cout << "a "   << st.a << endl;
 cout << "b "   << st.b << endl;
 cout << "c "   << st.c << endl;
 cout << "str " << st.str << endl;

Ini jelas bukan cara tercantik untuk melakukannya tetapi itu akan berhasil.


-2

Inilah contoh lengkap dari solusi masalah semacam itu

#include <bits/stdc++.h>
using namespace std;
pair<int,int> solve(int brr[],int n)
{
    sort(brr,brr+n);

    return {brr[0],brr[n-1]};
}

int main()
{
    int n;
    cin >> n;
    int arr[n];
    for(int i=0; i<n; i++)
    {
        cin >> arr[i];
    }

    pair<int,int> o=solve(arr,n);
    cout << o.first << " " << o.second << endl;

    return 0;
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.