C ++ फ़ंक्शन से कई मान लौटा रहा है


242

क्या C ++ फ़ंक्शन से कई मान वापस करने का एक पसंदीदा तरीका है? उदाहरण के लिए, एक फ़ंक्शन की कल्पना करें जो दो पूर्णांकों को विभाजित करता है और भागफल और शेष दोनों को वापस करता है। एक तरीका जो मैं आमतौर पर देखता हूं, वह है संदर्भ पैरामीटर का उपयोग करना:

void divide(int dividend, int divisor, int& quotient, int& remainder);

एक भिन्नता एक मान लौटाने और दूसरे को संदर्भ पैरामीटर के माध्यम से पारित करने के लिए है:

int divide(int dividend, int divisor, int& remainder);

एक अन्य तरीका यह होगा कि सभी परिणामों को शामिल करने के लिए एक संरचना घोषित की जाए और उसे वापस लौटाया जाए:

struct divide_result {
    int quotient;
    int remainder;
};

divide_result divide(int dividend, int divisor);

क्या इनमें से एक तरीका आम तौर पर पसंद किया जाता है, या अन्य सुझाव हैं?

संपादित करें: वास्तविक दुनिया कोड में, दो से अधिक परिणाम हो सकते हैं। वे विभिन्न प्रकार के भी हो सकते हैं।

जवाबों:


217

दो मूल्यों को वापस करने के लिए मैं एक std::pair(आमतौर पर टाइपडिफेल्ड) का उपयोग करता हूं । आपको दो से अधिक रिटर्न परिणामों के लिए boost::tuple(C ++ 11 और नए में, वहां std::tuple) देखना चाहिए ।

सी ++ 17 में संरचित बंधन की शुरुआत के साथ, वापसी std::tupleको संभवतः मानक स्वीकार किया जाना चाहिए।


12
टपल के लिए +1। एक संरचना में लौटने वाली बड़ी वस्तुओं के प्रदर्शन के प्रभाव को ध्यान में रखें।
Marcin

12
यदि आप ट्यूपल्स का उपयोग करने जा रहे हैं, तो उनका उपयोग जोड़े के लिए भी क्यों न करें। क्यों है खास मामला?
फेरुचियो

4
फ्रेड, होय बूस्ट :: टपल ऐसा कर सकते हैं :)
जोहान्स स्काउब -

46
C ++ 11 में, आप उपयोग कर सकते हैं std::tuple
फेरुशियो 10

14
यदि आप किसी फ़ंक्शन से कई मानों को स्वीकार करना चाहते हैं, तो ऐसा करने का एक सुविधाजनक तरीका std::tie stackoverflow.com/a/2573822/502144
fdermishin

176

C ++ 11 में आप कर सकते हैं:

#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;
}

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;
}

या संरचनाओं के साथ:

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
ट्यूपल्स को लौटाने वाले कार्यों से मेरी एक चिंता है। उपरोक्त फ़ंक्शन प्रोटोटाइप एक हेडर में है, तो मुझे कैसे पता चलेगा कि फ़ंक्शन परिभाषा को समझने के बिना पहले और दूसरे रिटर्न वाले मूल्यों का क्या मतलब है? भागफल-शेष या शेष-भागफल।
उचिया इटाची

7
@UchiaItachi फ़ंक्शन मापदंडों के लिए समान चिंता, आप उन्हें नाम दे सकते हैं, लेकिन भाषा उस पर भी लागू नहीं होती है, और पढ़ते समय पैरामीटर नाम कॉल साइट पर कोई मूल्य नहीं है। इसके अलावा, एक ही रिटर्न पर, आपके पास बस एक प्रकार है, लेकिन नाम होने से भी उपयोगी हो सकता है, ट्यूपल्स के साथ आप इस मुद्दे को सिर्फ दोगुना करते हैं, इसलिए, केवल यही नहीं, कई मायनों में स्व-प्रलेखित होने के बारे में भाषा का केवल यही नहीं है।
काली मिर्च_ मिर्च

1
यदि मैं स्पष्ट रूप से विभाजन के प्रकार () को निर्दिष्ट करना चाहता हूं तो अंतिम उदाहरण कैसे दिखेगा? क्या मैं फिर परिणाम को कहीं और परिभाषित करूंगा, या मैं इसे रिटर्न टाइप विनिर्देश में सही परिभाषित कर सकता हूं?
स्लाव

1
@ स्लाव आप फ़ंक्शन हस्ताक्षर में एक प्रकार को सही परिभाषित नहीं कर सकते हैं, आपको बाहर के प्रकार को घोषित करना होगा और इसे वापसी प्रकार के रूप में उपयोग करना होगा, जैसे कि यह सामान्य रूप से किया जाता है (बस structफ़ंक्शन बॉडी के बाहर लाइन को स्थानांतरित करें और autoफ़ंक्शन रिटर्न को प्रतिस्थापित करें result
काली मिर्च_ जूल

3
@pepper_chico फ़ंक्शन divideको अलग cpp फ़ाइल में रखना चाहते हैं तो क्या होगा ? मुझे त्रुटि मिलती है error: use of ‘auto divide(int, int)’ before deduction of ‘auto’। मैं इसे कैसे हल करूं?
एड्रियन

123

निजी तौर पर, मैं आम तौर पर कई कारणों से रिटर्न मापदंडों को नापसंद करता हूं:

  • यह आह्वान में हमेशा स्पष्ट नहीं होता है कि कौन से पैरामीटर इन्स हैं और कौन से बाहरी हैं
  • आपको आमतौर पर परिणाम को पकड़ने के लिए एक स्थानीय चर बनाना होगा, जबकि रिटर्न मान का उपयोग इनलाइन किया जा सकता है (जो कि एक अच्छा विचार हो सकता है या नहीं भी हो सकता है, लेकिन कम से कम आपके पास विकल्प है)
  • यह मुझे लगता है कि एक "डोर" और एक फंक्शन के लिए एक "आउट डोर" के लिए क्लीनर है - सभी इनपुट यहां जाते हैं, सभी आउटपुट वहां से बाहर आते हैं
  • मुझे अपनी तर्क सूचियों को यथासंभव छोटा रखना पसंद है

मुझे जोड़ी / टपल तकनीक के बारे में भी कुछ आरक्षण है। मुख्य रूप से, वापसी मूल्यों के लिए अक्सर कोई प्राकृतिक आदेश नहीं होता है। यह जानने के लिए कि कोड का पाठक कैसे है। परिणाम क्या भागफल या शेष है? और कार्यान्वयनकर्ता आदेश बदल सकता है, जो मौजूदा कोड को तोड़ देगा। यह विशेष रूप से कपटी है यदि मान एक ही प्रकार के होते हैं ताकि कोई संकलक त्रुटि या चेतावनी उत्पन्न न हो। दरअसल, ये तर्क मापदंडों को वापस करने के लिए भी लागू होते हैं।

यहाँ एक और कोड उदाहरण दिया गया है, यह थोड़ा कम तुच्छ है:

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;

क्या यह प्रिंट ग्रॉसपीड और कोर्स, या कोर्स और ग्राउंडस्पीड प्रिंट करता है? यह स्पष्ट नहीं है।

इसकी तुलना करें:

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;

मुझे लगता है कि यह स्पष्ट है।

इसलिए मुझे लगता है कि सामान्य रूप से मेरी पहली पसंद संरचना तकनीक है। जोड़ी / टुपल विचार कुछ मामलों में एक महान समाधान होने की संभावना है। जब संभव हो तो मैं वापसी के मापदंडों से बचना चाहूंगा।


1
इस structतरह की घोषणा करने का सुझाव Velocityएक अच्छा है। हालांकि, एक चिंता यह है कि यह नाम स्थान को प्रदूषित करता है। मुझे लगता है कि C ++ 11 के साथ, structएक लंबे प्रकार का नाम हो सकता है, और कोई भी उपयोग कर सकता है auto result = calculateResultingVelocity(...)
ह्यूगस

5
+1। एक फ़ंक्शन को किसी एक "चीज़" को वापस करना चाहिए , किसी तरह से आदेशित "चीजों का टपल" नहीं।
देवसोलर

1
मैं इस उत्तर में वर्णित कारणों के लिए std :: जोड़े / std :: tuples पर स्ट्रक्चर्स पसंद करता हूं। लेकिन मुझे नामस्थान "प्रदूषण" भी पसंद नहीं है। मेरे लिए आदर्श समाधान अनाम संरचना की तरह होगा struct { int a, b; } my_func();। यह इस रूप में इस्तेमाल किया जा सकता है auto result = my_func();:। लेकिन C ++ इसकी अनुमति नहीं देता है: "नए प्रकारों को एक वापसी प्रकार में परिभाषित नहीं किया जा सकता है"। इसलिए मुझे इस तरह की struct my_func_result_t
संरचनाएँ बनानी होंगी

2
@anton_rh: C ++ 14 के साथ स्थानीय प्रकारों को वापस करने की अनुमति देता है auto, इसलिए auto result = my_func();तुच्छ रूप से प्राप्य है।
०१५:

4
कुछ 15 साल पहले जब हमें बूस्ट मिला तो हमने टपल का इस्तेमाल किया क्योंकि यह काफी उपयोगी है। ओवरटाइम को हमने विशेष रूप से एक ही प्रकार के टुपल्स के लिए पठनीयता में असुविधा का अनुभव किया (जैसे कि टपल <डबल, डबल>; जो एक है)। तो हाल ही में हम एक छोटी POD संरचना को शुरू करने की आदत में अधिक हैं जहां कम से कम सदस्य चर का नाम कुछ समझदार इंगित करता है।
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

एसटीडी :: जोड़ी अनिवार्य रूप से आपकी संरचना समाधान है, लेकिन पहले से ही आपके लिए परिभाषित है, और किसी भी दो डेटा प्रकारों के अनुकूल होने के लिए तैयार है।


3
यह मेरे सरल उदाहरण के लिए काम करेंगे। सामान्य तौर पर, हालांकि, दो से अधिक मान वापस आ सकते हैं।
फ्रेड लार्सन

5
स्व-दस्तावेज भी नहीं। क्या आप याद कर सकते हैं कि D86 के लिए कौन सा x86 रजिस्टर शेष है?
मार्क

1
@ मर्क - मैं मानता हूं कि स्थितिगत समाधान कम बनाए रखने योग्य हो सकते हैं। आप "परमिट और बैफल" समस्या में भाग सकते हैं।
फ्रेड लार्सन

16

यह पूरी तरह से वास्तविक कार्य और कई मूल्यों के अर्थ और उनके आकार पर निर्भर है:

  • यदि वे आपके अंश उदाहरण के रूप में संबंधित हैं, तो मैं एक संरचना या वर्ग उदाहरण के साथ जाऊंगा।
  • यदि वे वास्तव में संबंधित नहीं हैं और उन्हें कक्षा / संरचना में वर्गीकृत नहीं किया जा सकता है, तो शायद आपको अपनी विधि को दो में बदलना चाहिए।
  • आपके द्वारा लौट रहे मूल्यों के इन-मेमरी आकार के आधार पर, आप एक पॉइंटर को क्लास के इंस्टेंस या स्ट्रक्चर पर लौटना चाहते हैं, या रेफरेंस पैरामीटर का उपयोग कर सकते हैं।

1
मुझे आपका जवाब पसंद है और आपकी आखिरी गोली मुझे कुछ याद दिलाती है जो मैंने अभी पढ़ा है कि मूल्य से गुजरना इस स्थिति को और अधिक जटिल बनाने के आधार पर बहुत तेजी से हो गया है ... cpp-next.com/archive/2009/08/want-speed-pass -by-मूल्य
ऋषि

12

इसके लिए OO समाधान एक अनुपात वर्ग बनाना है। यह कोई अतिरिक्त कोड नहीं लेगा (कुछ को बचाएगा), काफी क्लीनर / क्लियर होगा, और आपको कुछ अतिरिक्त रिफ्लेक्टर देगा जिससे आपको इस वर्ग के बाहर भी कोड साफ करने में मदद मिलेगी।

वास्तव में मुझे लगता है कि किसी ने एक संरचना को वापस करने की सिफारिश की, जो काफी करीब है, लेकिन इस इरादे को छुपाता है कि यह निर्माणकर्ता और कुछ तरीकों के साथ एक पूरी तरह से सोचा जाने वाला वर्ग होना चाहिए, वास्तव में, "विधि" जिसे आपने मूल रूप से उल्लेख किया था (वापसी के रूप में) जोड़ी) सबसे अधिक संभावना है कि इस वर्ग का एक सदस्य होना चाहिए जो खुद का उदाहरण देता है।

मुझे पता है कि आपका उदाहरण केवल एक "उदाहरण" था, लेकिन तथ्य यह है कि जब तक कि आपका कार्य किसी भी फ़ंक्शन से अधिक नहीं हो रहा है, यदि आप इसे कई मानों को वापस करना चाहते हैं, तो आप निश्चित रूप से एक ऑब्जेक्ट गायब कर रहे हैं।

काम के छोटे टुकड़ों को करने के लिए इन छोटे वर्गों को बनाने से डरो मत - यही ओओ का जादू है - आप इसे तब तक खत्म करते हैं जब तक कि हर विधि बहुत छोटी और सरल न हो और हर वर्ग छोटा और समझने योग्य हो।

एक और बात जो एक संकेतक होनी चाहिए थी कि कुछ गलत था: ओओ में आपके पास अनिवार्य रूप से कोई डेटा नहीं है - ओओ डेटा के आसपास से गुजरने के बारे में नहीं है, एक वर्ग को आंतरिक रूप से अपने स्वयं के डेटा को प्रबंधित करने और हेरफेर करने की आवश्यकता है, किसी भी डेटा पासिंग (एक्सेसर्स सहित) एक संकेत है कि आपको कुछ पुनर्विचार करने की आवश्यकता हो सकती है ..


10

C (और इसलिए C ++) मानक के साथ div, ldiv(और, C99 में lldiv) , <stdlib.h>(या <cstdlib>) से कार्य करने के लिए वापस लौटने के लिए मिसाल है ।

'रिटर्न वैल्यू और रिटर्न पैरामीटर्स का मिश्रण' आमतौर पर कम से कम साफ होता है।

एक फ़ंक्शन होने के बाद वापसी की स्थिति और रिटर्न मापदंडों के माध्यम से डेटा सी में समझदार है; यह C ++ में कम स्पष्ट रूप से समझदार है, जहां आप इसके बजाय विफलता की जानकारी को रिले करने के लिए अपवादों का उपयोग कर सकते हैं।

यदि दो से अधिक रिटर्न मान हैं, तो एक संरचना जैसा तंत्र संभवतः सबसे अच्छा है।


10

C ++ 17 के साथ आप एक अयस्क को अधिक अवाँछनीय / अकुशल मान (कुछ मामलों में) वापस कर सकते हैं । अचूक प्रकारों की वापसी की संभावना नए गारंटीकृत रिटर्न वैल्यू ऑप्टिमाइज़ेशन के माध्यम से आती है, और यह समुच्चय के साथ अच्छी तरह से रचना करती है , और जिसे टेम्पर्डेड कंस्ट्रक्टर कहा जा सकता है ।

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();
}

इसके बारे में सुंदर बात यह है कि यह किसी भी नकल या बढ़ने का कारण नहीं बनने की गारंटी है । आप उदाहरण के manyरूप में भी बना सकते हैं । अधिक जानकारी:

C ++ 17 वैरिएड टेम्पलेट 'कंस्ट्रक्शन डिडक्शन गाइड' के लिए वैरिएड एग्रीगेट (स्ट्रक्चर) और सिंटैक्स लौटाता है


6

कई मापदंडों को वापस करने के तरीकों का एक गुच्छा है। मैं बहकने वाला हूँ।

संदर्भ मापदंडों का उपयोग करें:

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

सूचक मापदंडों का उपयोग करें:

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

जिसका फायदा यह है कि आपको &कॉल-साइट पर करना पड़ता है , संभवतः लोगों को सचेत करना यह एक आउट-पैरामीटर है।

एक टेम्प्लेट लिखें और उसका उपयोग करें:

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

तो हम कर सकते हैं:

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

और सब अच्छा है। fooअब बोनस के रूप में पारित किसी भी मूल्य को पढ़ने में सक्षम नहीं है।

एक स्थान को परिभाषित करने के अन्य तरीके जो आप डेटा डाल सकते हैं निर्माण के लिए उपयोग किया जा सकता है out। उदाहरण के लिए, कहीं न कहीं चीजों का अनुकरण करने के लिए एक कॉलबैक।

हम एक संरचना लौटा सकते हैं:

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

C ++ के हर संस्करण में, और अंदर काम करता है यह भी अनुमति देता है:

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

शून्य लागत पर। पैरामीटर भी गारंटीकृत elision के लिए धन्यवाद स्थानांतरित नहीं किया जा सकता है।

हम वापस आ सकते हैं std::tuple:

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

जिसका नकारात्मक पहलू यह है कि मापदंडों का नाम नहीं है। यह अनुमति देता है:

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

भी। इसके पहले हम इसके बजाय कर सकते हैं:

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

जो अभी थोड़ा और अजीब है। हालांकि गारंटी की बात यहाँ नहीं है।

अजनबी क्षेत्र में जाना (और यह बाद में है out<>!), हम निरंतरता शैली का उपयोग कर सकते हैं:

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

और अब कॉलर करते हैं:

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

इस शैली का एक लाभ यह है कि आप स्मृति का प्रबंधन किए बिना मानों की एक समान संख्या (समान प्रकार के साथ) वापस कर सकते हैं:

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

valueकॉलबैक 500 बार जब आप कहा जा सकता है get_all_values( [&](int value){} )

शुद्ध पागलपन के लिए, आप निरंतरता पर एक निरंतरता का भी उपयोग कर सकते हैं।

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

जिसका उपयोग इस तरह दिखता है:

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

जो कई संबंधों के बीच अनुमति देगा resultऔर other

फिर से बिना मूल्य के, हम यह कर सकते हैं:

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

यहां, हम कॉलबैक को परिणामों की अवधि के साथ कहते हैं। इसे हम बार-बार भी कर सकते हैं।

इसका उपयोग करके, आपके पास एक फ़ंक्शन हो सकता है जो स्टैक से कोई आवंटन किए बिना कुशलतापूर्वक मेगाबाइट डेटा पास करता है।

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
}

अब, std::functionइसके लिए थोड़ा भारी है, क्योंकि हम शून्य-ओवरहेड नो-आवंटन वातावरण में ऐसा कर रहे हैं। इसलिए हम चाहते हैं function_viewकि कभी भी आवंटन न हो।

एक और उपाय है:

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

जहां कॉलबैक लेने और इसे लागू करने के fooबजाय , एक फ़ंक्शन देता है जो कॉलबैक लेता है।

foo (7) ([&] (int result, int other_result) {/ * कोड * /}); यह अलग-अलग ब्रैकेट होने से इनपुट मापदंडों से आउटपुट पैरामीटर को तोड़ता है।

के साथ variantऔरcoroutines, आप fooरिटर्न प्रकार (या सिर्फ रिटर्न प्रकार) के एक प्रकार का एक जनरेटर बना सकते हैं । वाक्यविन्यास अभी तय नहीं है, इसलिए मैं उदाहरण नहीं दूंगा।

संकेतों और स्लॉट्स की दुनिया में, एक फ़ंक्शन जो संकेतों के एक सेट को उजागर करता है:

template<class...Args>
struct broadcaster;

broadcaster<int, int> foo();

आपको fooवह काम करने की अनुमति देता है जो async काम करता है और परिणाम समाप्त होने पर प्रसारित करता है।

इस पंक्ति के नीचे हमारे पास विभिन्न प्रकार की पाइपलाइन तकनीकें हैं, जहाँ कोई फ़ंक्शन कुछ नहीं करता है, बल्कि डेटा को किसी तरह से जोड़ने की व्यवस्था करता है, और यह करना अपेक्षाकृत स्वतंत्र है।

foo( int_source )( int_dest1, int_dest2 );

तब तक यह कोड कुछ भी नहीं करता है जब तक int_sourceकि इसे प्रदान करने के लिए पूर्णांक न हों। जब यह होता है, int_dest1और int_dest2परिणामों को समझना शुरू करते हैं।


इस उत्तर में अन्य उत्तरों की तुलना में अधिक जानकारी है! विशेष रूप से, auto&&[result, other_result]=foo();टुपल्स और संरचनाओं को वापस करने वाले कार्यों के बारे में जानकारी । धन्यवाद!
jjmontes

मैं इस संपूर्ण उत्तर की सराहना करता हूं, खासकर जब से मैं अभी भी सी ++ 11 से अटका हुआ हूं और इसलिए कुछ और आधुनिक समाधानों का उपयोग नहीं कर सकता हूं जो अन्य लोग प्रस्तावित करते हैं।
18

5

वापसी मूल्य के लिए एक संरचना या एक वर्ग का उपयोग करें। उपयोग करना std::pairअभी के लिए काम कर सकता है, लेकिन

  1. यदि आप बाद में निर्णय लेते हैं कि आप अधिक जानकारी चाहते हैं तो यह अनम्य है;
  2. यह हेडर में फ़ंक्शन की घोषणा से बहुत स्पष्ट नहीं है कि क्या लौटाया जा रहा है और किस क्रम में है।

स्व-प्रलेखित सदस्य चर नामों के साथ एक संरचना लौटना संभवतः आपके फ़ंक्शन का उपयोग करने वाले किसी भी व्यक्ति के लिए कम बग-प्रवण होगा। एक पल के लिए मेरी सहकर्मी टोपी को रखना, आपकी divide_resultसंरचना मेरे लिए आसान है, आपके फ़ंक्शन का एक संभावित उपयोगकर्ता, तुरंत 2 सेकंड के बाद समझने के लिए। Ouput पैरामीटर या रहस्यमय जोड़े और ट्यूपल्स के साथ गड़बड़ करने से पढ़ने के लिए अधिक समय लगेगा और गलत तरीके से उपयोग किया जा सकता है। और सबसे अधिक संभावना है कि फ़ंक्शन का उपयोग करने के बाद भी कुछ बार मुझे अभी भी तर्कों का सही क्रम याद नहीं होगा।


4

यदि आपका फ़ंक्शन संदर्भ के माध्यम से एक मान लौटाता है, तो संकलक अन्य कार्यों को कॉल करते समय इसे एक रजिस्टर में संग्रहीत नहीं कर सकता है क्योंकि, सैद्धांतिक रूप से, पहला फ़ंक्शन एक वैश्विक रूप से सुलभ चर में इसके लिए पारित चर का पता बचा सकता है, और किसी भी उप-कार्य को बुलाया जा सकता है इसे बदल दें, इसलिए संकलक के पास (1) अन्य फ़ंक्शन को कॉल करने से पहले रजिस्टरों से मान को वापस मेमोरी में सेव कर लेगा और (2) इसे दोबारा पढ़े जब इस तरह की किसी भी कॉल के बाद फिर से मेमोरी से इसकी आवश्यकता हो।

यदि आप संदर्भ से लौटते हैं, तो आपके कार्यक्रम का अनुकूलन प्रभावित होगा


4

यहाँ, मैं एक प्रोग्राम लिख रहा हूँ जो c ++ में कई मान (दो से अधिक मान) लौटा रहा है। यह कार्यक्रम c ++ 14 (G ++ 4.9.2) में निष्पादन योग्य है। कार्यक्रम एक कैलकुलेटर की तरह है।

#  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;
}

तो, आप स्पष्ट रूप से समझ सकते हैं कि इस तरह आप एक फ़ंक्शन से कई मान वापस कर सकते हैं। एसटीडी :: जोड़ी का उपयोग करते हुए केवल 2 मान लौटाए जा सकते हैं :: टपल दो मूल्यों से अधिक वापस आ सकता है।


4
C ++ 14 के साथ आप इसे और भी साफ करने के लिए autoरिटर्न टाइप का उपयोग कर सकते हैं cal। (IMO)।
sfjac

3

मैं इस तरह के कार्यों में आउट-वैल का उपयोग करता हूं, क्योंकि मैं सफलता / त्रुटि कोड लौटाने वाले फ़ंक्शन के प्रतिमान से चिपकता हूं और मुझे चीजों को समान रखना पसंद है।


2

विकल्प में सरणियाँ, जनरेटर और नियंत्रण के व्युत्क्रम शामिल हैं , लेकिन यहां कोई भी उपयुक्त नहीं है।

कुछ (उदाहरण के लिए Win32 में Microsoft) सादगी के लिए संदर्भ मापदंडों का उपयोग करते हैं, क्योंकि यह स्पष्ट है कि कौन आवंटित करता है और यह कैसे स्टैक पर दिखेगा, संरचनाओं के प्रसार को कम करता है, और सफलता के लिए एक अलग वापसी मूल्य की अनुमति देता है।

"शुद्ध" प्रोग्रामर, struct पसंद करते हैं यह सोचते हैं यह है समारोह मूल्य (मामले यहाँ है के रूप में), बल्कि कुछ है कि के समारोह से संयोग से छुआ है। यदि आपके पास अधिक जटिल प्रक्रिया थी, या राज्य के साथ कुछ था, तो आप शायद संदर्भों का उपयोग करेंगे (यह मानते हुए कि आपके पास कक्षा का उपयोग न करने का एक कारण है)।


2

मैं कहूंगा कि कोई पसंदीदा तरीका नहीं है, यह सब इस बात पर निर्भर करता है कि आप प्रतिक्रिया के साथ क्या करने जा रहे हैं। यदि परिणाम आगे की प्रक्रिया में एक साथ उपयोग किए जाने वाले हैं तो संरचनाएं समझ में आती हैं, यदि मैं पास नहीं करूंगा तो व्यक्तिगत संदर्भों के रूप में जब तक कि फ़ंक्शन को एक समग्र कथन में उपयोग नहीं किया जा रहा है:

x = divide( x, y, z ) + divide( a, b, c );

मैं अक्सर एक नई संरचना को लौटाने की कॉपी ओवरहेड द्वारा पास होने के बजाय पैरामीटर सूची में संदर्भ द्वारा 'आउट संरचनाओं' को पारित करना चुनता हूं (लेकिन यह छोटे सामान को पसीना आ रहा है)।

void divide(int dividend, int divisor, Answer &ans)

क्या पैरामीटर भ्रामक हैं? संदर्भ के रूप में भेजा गया एक पैरामीटर बताता है कि मान बदलने जा रहा है (एक कास्ट संदर्भ के विपरीत)। समझदार नामकरण भी भ्रम को दूर करता है।


1
मुझे लगता है कि यह थोड़ा भ्रमित करने वाला है। कोई ऐसा रीडिंग कोड जो इसे कहता है, "डिवाइड (ए, बी, सी)" देखता है। कोई संकेत नहीं है कि जब तक वे हस्ताक्षर नहीं करते हैं तब तक सी एक आउटलुक है। लेकिन यह इस प्रश्न के लिए विशेष रूप से बजाय गैर-कास्ट संदर्भ params का एक सामान्य डर है।
स्टीव जेसप

2

आप कई रिटर्न वैल्यू वाले फ़ंक्शन पर ज़ोर क्यों देते हैं? OOP के साथ आप एक एकल रिटर्न मान के साथ एक नियमित फ़ंक्शन की पेशकश करने वाले एक वर्ग का उपयोग कर सकते हैं, और नीचे की तरह किसी भी अतिरिक्त "रिटर्न मान" की संख्या। फायदा यह है कि कॉलर के पास अतिरिक्त डेटा सदस्यों को देखने का एक विकल्प है, लेकिन ऐसा करने की आवश्यकता नहीं है। यह जटिल डेटा बेस या नेटवर्किंग कॉल के लिए पसंदीदा तरीका है, जहां बहुत सी अतिरिक्त वापसी जानकारी की आवश्यकता हो सकती है, जब मामले में त्रुटियां होती हैं।

आपके मूल प्रश्न का उत्तर देने के लिए, इस उदाहरण में भागफल को लौटाने की एक विधि है, जो कि ज्यादातर कॉलर्स की आवश्यकता हो सकती है, और इसके अलावा, विधि कॉल के बाद, आप शेष को डेटा सदस्य के रूप में प्राप्त कर सकते हैं।

class div{
   public:
      int remainder;

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

1
मुझे लगता है कि ऐसे मामले हैं जहां यह अक्षम है। उदाहरण के लिए, आपके पास लूप के लिए एक एकल है जो कई रिटर्न मान उत्पन्न करता है। यदि आप उन मानों को अलग-अलग कार्यों में विभाजित करते हैं, तो आपको प्रत्येक मान के लिए एक बार लूप के माध्यम से चलना होगा।
जिगगंजर

1
@jiggunjer आप एक बार लूप चला सकते हैं और अलग-अलग वर्ग डेटा सदस्यों में कई रिटर्न मान स्टोर कर सकते हैं। यह OOP अवधारणा के लचीलेपन को रेखांकित करता है।
रोलैंड

2

कई मानों को वापस करने के बजाय, उनमें से एक को वापस लौटाएं और उदाहरण के लिए आवश्यक फ़ंक्शन में दूसरों का संदर्भ बनाएं:

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

क्या मैंने इस प्रश्न में ही इसका उल्लेख नहीं किया है? इसके अलावा, मेरे जवाब में मेरी आपत्तियां देखें ।
फ्रेड लार्सन

1

बूस्ट टपल एक फ़ंक्शन से एक से अधिक मान लौटाने की सामान्यीकृत प्रणाली के लिए मेरी पसंदीदा पसंद होगी।

संभावित उदाहरण:

include "boost/tuple/tuple.hpp"

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

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

1

हम फ़ंक्शन को इस तरह घोषित कर सकते हैं, यह एक संरचना प्रकार उपयोगकर्ता परिभाषित चर या इसके लिए एक संकेतक लौटाता है। और एक संरचना की संपत्ति से, हम जानते हैं कि सी में एक संरचना विषम प्रकार के कई मूल्यों (यानी एक इंट चर, चार चार चर, दो फ्लोट चर और इतने पर…) को धारण कर सकती है।


1

मैं इसे केवल संदर्भ के द्वारा करूंगा यदि यह केवल कुछ रिटर्न मान है लेकिन अधिक जटिल प्रकारों के लिए आप इसे केवल इस तरह भी कर सकते हैं:

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!
}

"स्थैतिक" का उपयोग इस संकलन इकाई में वापसी प्रकार के दायरे को सीमित करने के लिए यदि यह केवल एक अस्थायी रिटर्न प्रकार होने के लिए है।

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

यह निश्चित रूप से इसे करने का सबसे सुंदर तरीका नहीं है, लेकिन यह काम करेगा।


-2

इस तरह की समस्या समाधान का एक पूर्ण उदाहरण यहां दिया गया है

#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;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.