সি ++ ফাংশন থেকে একাধিক মান প্রত্যাবর্তন


242

সি ++ ফাংশন থেকে একাধিক মান ফেরত দেওয়ার কোনও পছন্দসই উপায় আছে কি? উদাহরণস্বরূপ, এমন একটি ফাংশনটি কল্পনা করুন যা দুটি পূর্ণসংখ্যা ভাগ করে এবং ভাগফল এবং বাকী উভয়ই প্রদান করে। আমি সাধারণত যেভাবে দেখি তা হল রেফারেন্স প্যারামিটারগুলি ব্যবহার করা:

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

এই উপায়গুলির মধ্যে একটি সাধারণত পছন্দসই হয়, বা অন্য পরামর্শ আছে?

সম্পাদনা: রিয়েল-ওয়ার্ল্ড কোডে, দুটিরও বেশি ফলাফল হতে পারে। এগুলি বিভিন্ন ধরণেরও হতে পারে।

উত্তর:


216

দুটি মান প্রত্যাবর্তনের জন্য আমি একটি std::pair(সাধারণত টাইপিডেফ) ব্যবহার করি । আপনি তাকান উচিত boost::tuple(C ++ 11 এবং নতুন, আছে std::tupleচেয়ে বেশি দুই আছে এমন ফলাফল জন্য)।

সি ++ 17 এ স্ট্রাকচার্ড বাইন্ডিংয়ের প্রবর্তনের সাথে সাথে ফিরে আসার std::tupleবিষয়টি সম্ভবত গ্রহণযোগ্য স্ট্যান্ডার্ড হওয়া উচিত।


12
টিপল জন্য +1। রেফারেন্স দ্বারা পাস করে কোনও কাঠামোয় ফিরে আসা বড় অবজেক্টগুলির পারফরম্যান্স র্যামফিকেশনগুলি মনে রাখবেন।
মার্সিন

12
আপনি যদি টিপলস ব্যবহার করতে যাচ্ছেন তবে কেন সেগুলি জোড়া হিসাবে ব্যবহার করবেন না। কেন একটি বিশেষ মামলা আছে?
ফেরুরসিও

4
ফ্রেড, হ্যাঁ বুস্ট :: টিপল এটি করতে পারে :)
জোহানেস স্কাউব - লিটব

46
সি ++ 11 এ আপনি ব্যবহার করতে পারেন std::tuple
ফেরুঁচিও

14
আপনি যদি একটি ফাংশন থেকে একাধিক মান গ্রহণ করতে চান তাহলে, এই কাজ করার সুবিধাজনক উপায় ব্যবহার করা std::tie stackoverflow.com/a/2573822/502144
fdermishin

176

সি ++ 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;
}

সি ++ 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
@ উচিয়াআইতাচি ফাংশন প্যারামিটারগুলির জন্য একই উদ্বেগ, আপনি তাদের নাম দিতে পারেন, তবে ভাষা এটি প্রয়োগ করে না এবং পড়ার সময় পলমিতিগুলির নামগুলি কল সাইটে কোনও মূল্য রাখে না। এছাড়াও, একক রিটার্নে, আপনার কেবল একটি টাইপ রয়েছে, তবে নামটিও কার্যকর হতে পারে, টিপলসের সাহায্যে আপনি কেবল দ্বিগুণ দ্বিগুণ হয়ে যান, সুতরাং, কেবলমাত্র এটিই নয়, ভাষার বেশ কয়েকটি উপায়ে স্ব-ডকুমেন্টেড হওয়া সম্পর্কিতও অভাব রয়েছে।
মরিচ_চিকো

1
আমি উদাহরণটি কীভাবে দেখব যদি আমি স্পষ্টভাবে বিভাজনের ফিরতি প্রকার () উল্লেখ করতে চাই? আমি কি অন্য কোথাও ফলাফল সংজ্ঞায়িত করব, অথবা আমি এটির পরিবর্তনের ধরণের নির্দিষ্টকরণে সংজ্ঞা দিতে পারি?
স্লাভা

1
@Slava আপনি ফাংশন স্বাক্ষর এ একটি টাইপ অধিকার সংজ্ঞায়িত করতে পারবে না, আপনি টাইপ বাহিরে ঘোষণা ও, রিটার্ন টাইপ হিসাবে এটি ব্যবহার মত স্বাভাবিকভাবে সম্পন্ন (ঠিক সরাতে হবে structফাংশন শরীরের বাইরে লাইন এবং প্রতিস্থাপন autoসঙ্গে ফাংশন রিটার্ন result
গোলমরিচ_চিকো

3
@ পিপার_চিকো যদি ফাংশনের সংজ্ঞাটি divideআলাদা সিপিপি ফাইলের মধ্যে রাখতে চান তবে? আমি ত্রুটি পেয়েছি 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 টি। একটি ফাংশন ফেরত পাঠাবেন এক "জিনিস", না একটি একরকম-বরাত "জিনিস tuple"।
দেবসোলার

1
এই উত্তরে বর্ণিত কারণে আমি স্টাডি :: জোড় / স্টাডি :: টিপলসের উপর স্ট্রাক্ট পছন্দ করি। তবে আমি নেমস্পেস "দূষণ "ও পছন্দ করি না। আমার জন্য আদর্শ সমাধানটি বেনামে কাঠামোর মতো ফিরে আসবে struct { int a, b; } my_func();। এই হিসাবে ব্যবহার করা যেতে পারে: auto result = my_func();। তবে সি ++ এটি অনুমোদন করে না: "নতুন ধরণের কোনও রিটার্নের ধরণে সংজ্ঞায়িত করা যায় না"। সুতরাং আমাকে এই জাতীয় struct my_func_result_t
স্ট্রাইক

2
@ অ্যান্টন_আরহ: সি ++ ১৪ এর সাথে স্থানীয় প্রকারগুলি ফিরে আসার অনুমতি দেয় auto, তাই auto result = my_func();তুচ্ছভাবে পাওয়া যায়।
iljarn

4
প্রায় 15 বছর আগে যখন আমরা বুস্ট আবিষ্কার করি তখন আমরা টিউপলটি বেশ ব্যবহার করি কারণ এটি বেশ কার্যকর। ওভারটাইম আমরা পাঠযোগ্যতার অসুবিধাগুলি বিশেষত একই ধরণের টিপলগুলির (যেমন টুপল <ডাবল, ডাবল>; কোনটি) এর অভিজ্ঞতা পেয়েছি experienced সুতরাং ইদানীং আমরা একটি ছোট পিওডি কাঠামো প্রবর্তন করার অভ্যাসে বেশি আছি যেখানে কমপক্ষে সদস্যের নাম ভেরিয়েবলটি বোধগম্য কিছু বোঝায়।
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 :: জোড়া মূলত আপনার স্ট্রাক্ট সমাধান, তবে ইতিমধ্যে আপনার জন্য সংজ্ঞায়িত এবং যে কোনও দুটি ডেটা ধরণের সাথে খাপ খাইয়ে নিতে প্রস্তুত।


3
এটি আমার সাধারণ উদাহরণের জন্য কাজ করবে। সাধারণভাবে, তবে দুটিরও বেশি মান ফিরে আসতে পারে।
ফ্রেড লারসন

5
স্ব-ডকুমেন্টিংও নয়। আপনি কী x86 রেজিস্টার ডিআইভির বাকী অংশ মনে করতে পারেন?
চিহ্নিত করুন

1
@ মার্ক - আমি একমত যে অবস্থানগত সমাধানগুলি কম রক্ষণাবেক্ষণযোগ্য হতে পারে। আপনি "পারমিট এবং বাকল" সমস্যাটি চালিয়ে যেতে পারেন।
ফ্রেড লারসন

16

এটি সম্পূর্ণরূপে আসল ফাংশন এবং একাধিক মানগুলির অর্থ এবং তাদের আকারের উপর নির্ভর করে:

  • যদি সেগুলি আপনার ভগ্নাংশের উদাহরণের মতো সম্পর্কিত হয় তবে আমি কোনও কাঠামো বা শ্রেণীর উদাহরণ দিয়ে যেতে পারি।
  • যদি তারা সত্যিকারের সাথে সম্পর্কিত না হয় এবং একটি শ্রেণি / কাঠামোয় গ্রুপবদ্ধ করা যায় না তবে সম্ভবত আপনার পদ্ধতিটি দুটিতে সংশোধন করা উচিত।
  • আপনি যে মানগুলি ফিরে আসছেন তার ইন-মেমরি আকারের উপর নির্ভর করে আপনি কোনও শ্রেণীর উদাহরণ বা কাঠামোতে কোনও পয়েন্টারটি ফিরে আসতে চাইতে পারেন, বা রেফারেন্স পরামিতি ব্যবহার করতে পারেন।

1
আমি আপনার উত্তরটি পছন্দ করি এবং আপনার শেষ বুলেটটি আমাকে এমন কিছু মনে করিয়ে দেয় যা আমি কেবল পড়েছি যে পরিস্থিতিটিকে আরও জটিল করে তুলেছে
ঋষি

12

এর জন্য ওও সমাধানটি একটি অনুপাত শ্রেণি তৈরি করা। এটি কোনও অতিরিক্ত কোড নেবে না (কিছু সাশ্রয় করবে), উল্লেখযোগ্যভাবে ক্লিনার / ক্লিয়ারার হবে এবং আপনাকে এই শ্রেণীর বাইরেও কোড সাফ করার সুযোগ দেয় এমন কিছু অতিরিক্ত রিফ্যাক্টরিংস দেয়।

আসলে আমি মনে করি কেউ কাঠামো ফেরত দেওয়ার পরামর্শ দিয়েছিলেন, এটি যথেষ্ট কাছাকাছি কিন্তু নির্ধারক এবং কয়েকটি পদ্ধতির সাথে এটি সম্পূর্ণ চিন্তা-ভাবনা শ্রেণি হওয়া দরকার এই উদ্দেশ্যটি লুকিয়ে রাখে, আসলে, আপনি মূলত "পদ্ধতি" যা আপনি উল্লেখ করেছেন (প্রত্যাবর্তন হিসাবে জোড়) সম্ভবত নিজের উদাহরণ দিয়ে ফিরে আসা এই শ্রেণীর সদস্য হওয়া উচিত।

আমি জানি আপনার উদাহরণটি কেবল একটি "উদাহরণ" ছিল, তবে সত্যটি হ'ল যদি আপনার ফাংশনটি কোনও ফাংশনের চেয়ে বেশি কাজ না করে তবে আপনি যদি একাধিক মান ফিরিয়ে নিতে চান তবে আপনি অবশ্যই কোনও অবজেক্ট হারিয়ে ফেলছেন।

ছোট ছোট টুকরো টুকরো টুকরো কাজ করার জন্য এই ক্ষুদ্র ক্লাসগুলি তৈরি করতে ভয় করবেন না - এটি ওয়ের যাদু - আপনি যতক্ষণ না প্রতিটি পদ্ধতি খুব ছোট এবং সাধারণ এবং প্রতিটি শ্রেণিই ছোট এবং বোধগম্য না হওয়া পর্যন্ত আপনি এটিকে ভেঙে ফেলবেন।

আর একটি জিনিস যা সূচক হওয়া উচিত ছিল যা কিছু ভুল ছিল: ওওতে আপনার কাছে কোনও ডেটা নেই - ওও ডেটা চারপাশের বিষয়ে নয়, একটি শ্রেণিকে অভ্যন্তরীণভাবে তার নিজস্ব ডেটা পরিচালনা এবং পরিচালনা করতে হবে, কোনও ডেটা পাসিং (অ্যাক্সেসর সহ) এটি এমন একটি চিহ্ন যা আপনাকে কোনও কিছু নিয়ে পুনর্বিবেচনার প্রয়োজন হতে পারে ..


10

সেখানে সি কাঠামো ফিরে (এবং অত: পর সি ++) সহ স্ট্যান্ডার্ড জন্য নজির নেই div, ldiv(এবং, C99 এ, lldivথেকে) ফাংশন <stdlib.h>(অথবা <cstdlib>)।

'রিটার্ন ভ্যালু এবং রিটার্ন প্যারামিটারগুলির মিশ্রণ' সাধারণত কমপক্ষে পরিষ্কার থাকে।

ফাংশন থাকা কোনও স্থিতি ফেরত দেয় এবং রিটার্ন পরামিতিগুলির মাধ্যমে ডেটা রিটার্ন করে সি-তে বোধগম্য; এটি সি ++ তে কম স্পষ্টভাবে বোধগম্য যেখানে আপনি পরিবর্তে ব্যর্থতার তথ্য রিলে করতে ব্যতিক্রম ব্যবহার করতে পারেন।

যদি দুটিরও বেশি রিটার্ন মান থাকে তবে একটি কাঠামোর মতো ব্যবস্থাই সম্ভবত সেরা।


10

সি ++ 17 এর মাধ্যমে আপনি আরও আকরিকটি আরও অচল / অব্যর্থযোগ্য মান (কিছু ক্ষেত্রে) ফিরে আসতে পারেন । সম্ভাবনা unmovable ধরনের নতুন নিশ্চিত ফেরত মান অপ্টিমাইজেশান মাধ্যমে আসা ফিরে যাওয়ার, এবং এটি সঙ্গে চমত্কারভাবে composes দলা , এবং কি বলা যেতে পারে টেমপ্লেট করা কনস্ট্রাকটর

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

wh + সি ++ এর প্রতিটি সংস্করণে এবং ঠিক আছে এটি অনুমতি দেয়:

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

শূন্য ব্যয়ে গ্যারান্টিযুক্ত এলিজেন্সের জন্য প্যারামিটারগুলি এমনকি সরানো যায় না।

আমরা একটি ফিরে আসতে পারে 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 ফলাফল, অন্যান্য অন্যান্য ফলাফল) {/ * কোড * /}); এটি পৃথক বন্ধনী রেখে ইনপুট পরামিতিগুলি থেকে আউটপুট প্যারামিটারগুলি ভেঙে দেয়।

সাথে variantএবংকর্টাইনগুলি, আপনি fooরিটার্নের ধরণের (বা কেবলমাত্র রিটার্নের ধরণের) বৈকল্পিকের একটি জেনারেটর তৈরি করতে পারেন। বাক্য গঠনটি এখনও ঠিক হয়নি, তাই আমি উদাহরণ দেব না।

সংকেত এবং স্লট জগতে, একটি ফাংশন যা সংকেতগুলির একটি সেট প্রকাশ করে:

template<class...Args>
struct broadcaster;

broadcaster<int, int> foo();

আপনাকে এমন একটি তৈরি করতে দেয় fooযা অ্যাসিঙ্ক কাজ করে এবং ফলাফলটি শেষ হওয়ার পরে এটি সম্প্রচার করে।

এই লাইনের নীচে আমাদের কাছে পাইপলাইনের বিভিন্ন কৌশল রয়েছে, যেখানে কোনও ফাংশন কিছু না করে বরং কোনও উপায়ে ডেটা সংযুক্ত হওয়ার ব্যবস্থা করে, এবং করা অপেক্ষাকৃত স্বতন্ত্র।

foo( int_source )( int_dest1, int_dest2 );

তারপর এই কোড না কি কিছু না হওয়া পর্যন্ত int_sourceএটা প্রদান পূর্ণসংখ্যার হয়েছে। এটি যখন হয়ে যায় int_dest1এবং int_dest2ফলাফলগুলি পাওয়া শুরু করুন start


এই উত্তরে অন্যান্য উত্তরগুলির চেয়ে আরও বেশি তথ্য রয়েছে! বিশেষত, auto&&[result, other_result]=foo();উভয় tuples এবং কাঠামো ফাংশন জন্য তথ্য । ধন্যবাদ!
jjmontes

আমি এই সম্পূর্ণ উত্তরটির প্রশংসা করি, বিশেষত যেহেতু আমি এখনও সি ++ 11 এর সাথে আটকে আছি এবং সেইজন্য অন্যান্য লোকেরা যে প্রস্তাব দেয় সেগুলি আরও আধুনিক সমাধানগুলি ব্যবহার করতে পারে না।
গাইজিজমো

5

প্রত্যাবর্তনের মানের জন্য স্ট্রাক্ট বা শ্রেণি ব্যবহার করুন। ব্যবহার std::pairআপাতত কাজ করতে পারে, কিন্তু

  1. আপনি যদি আরও সিদ্ধান্ত ফেরত চান তা পরে সিদ্ধান্ত নিলে তা জটিল নয়;
  2. শিরোনামে ফাংশনটির ঘোষণার থেকে এটি পরিষ্কার নয় যে কী ফিরিয়ে দেওয়া হচ্ছে এবং কোন ক্রমে।

স্ব-ডকুমেন্টিং সদস্য ভেরিয়েবলের নামগুলির সাথে কোনও কাঠামো ফিরে পাওয়া আপনার ফাংশনটি যে কেউ ব্যবহার করছেন তার পক্ষে কম বাগ প্রবণ হতে পারে। আমার সহকর্মীর টুপিটি এক মুহুর্তের জন্য রেখে দেওয়া, আপনার divide_resultকাঠামোটি আমার পক্ষে সহজ, আপনার কার্যকারিতার সম্ভাব্য ব্যবহারকারী, এটি 2 সেকেন্ড পরে তাত্ক্ষণিকভাবে বুঝতে। আউটপুট প্যারামিটার বা রহস্যময় জুটি এবং টিপলসগুলির সাথে চারপাশে জগাখিচির পাঠ পড়তে আরও সময় লাগবে এবং ভুলভাবে ব্যবহার করা যেতে পারে। এবং সম্ভবত বেশ কয়েকবার ফাংশনটি ব্যবহার করার পরেও আমি এখনও তর্কগুলির সঠিক ক্রমটি মনে করতে পারি না।


4

যদি আপনার ফাংশনটি রেফারেন্সের মাধ্যমে কোনও মান ফেরত দেয় তবে সংকলক অন্যান্য ফাংশনগুলিকে কল করার সময় এটি কোনও রেজিস্টারে সংরক্ষণ করতে পারে না কারণ তাত্ত্বিকভাবে, প্রথম ফাংশনটি বিশ্বব্যাপী অ্যাক্সেসযোগ্য ভেরিয়েবলের কাছে দেওয়া ভেরিয়েবলের ঠিকানাটি সংরক্ষণ করতে পারে এবং যে কোনও সাবসিকিউন্টি নামে পরিচিত ফাংশনগুলি হতে পারে এটি পরিবর্তন করুন, সুতরাং সংকলকটিতে (1) অন্যান্য ফাংশনগুলি কল করার আগে রেজিস্টারগুলি থেকে মেমরিতে ফিরে মান সংরক্ষণ করতে হবে এবং (২) এ জাতীয় কোনও কল করার পরে যখন মেমরি থেকে আবার প্রয়োজন হয় তখন এটি পুনরায় পড়ুন।

আপনি যদি রেফারেন্স দিয়ে ফিরে যান, আপনার প্রোগ্রামের অপ্টিমাইজেশন ক্ষতিগ্রস্থ হবে


4

এখানে, আমি এমন একটি প্রোগ্রাম লিখছি যা সি ++ এ একাধিক মান (দুইটির বেশি মান) প্রদান করবে। এই প্রোগ্রামটি সি ++ 14 (জি ++ 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;
}

সুতরাং, আপনি পরিষ্কারভাবে বুঝতে পারবেন যে এই পদ্ধতিতে আপনি কোনও ফাংশন থেকে একাধিক মান ফিরিয়ে দিতে পারেন। std :: জোড়া ব্যবহার করে শুধুমাত্র 2 টি মান ফেরত পাওয়া যাবে যখন std :: tuple দুটির বেশি মান প্রদান করতে পারে।


4
সি ++ 14 এর মাধ্যমে আপনি আরও পরিষ্কার করতে autoরিটার্ন টাইপও ব্যবহার করতে পারেন cal। (আইএমও)।
sfjac

3

আমি এই জাতীয় ফাংশনগুলিতে আউট-ভ্যালস ব্যবহার করার ঝোঁক করি, কারণ আমি সাফল্য / ত্রুটি কোডগুলি ফিরিয়ে ফাংশনটির দৃষ্টান্তের সাথে লেগে আছি এবং আমি জিনিসগুলি অভিন্ন রাখতে পছন্দ করি।


2

বিকল্পগুলির মধ্যে অ্যারে, জেনারেটর এবং নিয়ন্ত্রণের বিপরীকরণ অন্তর্ভুক্ত রয়েছে তবে এখানে কোনওটিই উপযুক্ত নয়।

কিছু (যেমন historicalতিহাসিক উইন 32-তে মাইক্রোসফ্ট) সরলতার জন্য রেফারেন্স পরামিতিগুলি ব্যবহার করার প্রবণতা রয়েছে কারণ কে বরাদ্দ করে এবং কীভাবে এটি স্ট্যাকের দিকে নজর দেবে, কাঠামোগুলির বিস্তারকে হ্রাস করে এবং সাফল্যের জন্য পৃথক ফেরতের মানকে মঞ্জুরি দেয়।

"বিশুদ্ধ" প্রোগ্রামার, struct হয় পছন্দ অভিমানী এটা হয় ফাংশন মান (কেস এখানে হিসাবে), বরং এমন কিছু বিষয় যা এর ফাংশন দ্বারা প্রসঙ্গক্রমে স্পর্শ করে। আপনার যদি আরও জটিল প্রক্রিয়া থাকে বা রাষ্ট্র সহ কিছু থাকে তবে আপনি সম্ভবত উল্লেখগুলি ব্যবহার করতেন (ধরে নিবেন যে কোনও শ্রেণি ব্যবহার না করার কারণ রয়েছে)।


2

আমি বলব কোনও পছন্দসই পদ্ধতি নেই, এটি সমস্ত নির্ভর করে আপনি প্রতিক্রিয়াটি নিয়ে কী করতে যাচ্ছেন on যদি ফলাফলগুলি আরও প্রক্রিয়াজাতকরণে একসাথে ব্যবহার করা হয় তবে কাঠামোগুলি অর্থবহ হয়, যদি আমি পৃথক রেফারেন্স হিসাবে পাস না করতাম তবে যদি ফাংশনটি কোনও যৌগিক বিবৃতিতে ব্যবহৃত না হত:

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

আমি প্রায়শই কোনও নতুন কাঠামো ফেরত দেওয়ার অনুলিপি সহ অনুলিপি পাসের পরিবর্তে প্যারামিটার তালিকায় রেফারেন্স দিয়ে 'স্ট্রাকচারগুলি আউট করা' পছন্দ করি (তবে এটি ছোট জিনিসকে ঘামছে)।

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

বিভ্রান্তিকর পরামিতি আছে? রেফারেন্স হিসাবে প্রেরিত একটি প্যারামিটারের সাহায্যে মান পরিবর্তিত হতে চলেছে (কনস্টের রেফারেন্সের বিপরীতে)। বোধগম্য নামকরণও বিভ্রান্তি দূর করে।


1
আমি মনে করি এটি কিছুটা বিভ্রান্তিকর। কেউ পড়ার কোড যা এটিকে কল করে "বিভাজন (ক, খ, গ);" sees সিটির স্বাক্ষর না হওয়া পর্যন্ত সি এর ফলাফল বলে কোনও চিহ্ন নেই। তবে এটি এই প্রশ্নের নির্দিষ্ট চেয়ে বরং অ-নিরপেক্ষ রেফারেন্স প্যারামগুলির একটি সাধারণ ভয়।
স্টিভ জেসোপ

2

আপনি কেন একাধিক রিটার্ন মান সহ কোনও ফাংশনটিতে জোর দিচ্ছেন? ওওপি দিয়ে আপনি একটি একক রিটার্ন মান, এবং নীচের মত অতিরিক্ত "রিটার্ন মান" সংখ্যার সাথে নিয়মিত ফাংশন সরবরাহ করে এমন একটি ক্লাস ব্যবহার করতে পারেন। সুবিধাটি হ'ল কলারের অতিরিক্ত ডেটা সদস্যদের দিকে নজর দেওয়ার পছন্দ রয়েছে তবে এটি করার প্রয়োজন হয় না। জটিল ডেটা বেস বা নেটওয়ার্কিং কলগুলির জন্য এটি পছন্দসই পদ্ধতি, যেখানে ত্রুটি দেখা দিলে প্রচুর অতিরিক্ত রিটার্ন তথ্য প্রয়োজন হতে পারে।

আপনার মূল প্রশ্নের উত্তর দেওয়ার জন্য, এই উদাহরণটিতে ভাগফলটি ফেরত দেওয়ার একটি পদ্ধতি রয়েছে যা বেশিরভাগ কলার প্রয়োজন হতে পারে এবং উপরন্তু, পদ্ধতি কল করার পরে, আপনি বাকীটি ডেটা সদস্য হিসাবে পেতে পারেন।

class div{
   public:
      int remainder;

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

1
আমি মনে করি এমন কেস রয়েছে যেখানে এটি অদক্ষ। উদাহরণস্বরূপ আপনার কাছে লুপের জন্য একক রয়েছে যা বেশ কয়েকটি রিটার্ন মান উত্পন্ন করে। যদি আপনি এই মানগুলি পৃথক ফাংশনে বিভক্ত করেন তবে প্রতিটি মানের জন্য আপনাকে একবার লুপের মধ্য দিয়ে চলতে হবে।
jiggunjer

1
@ জিগগুনজার আপনি একবার লুপটি চালাতে পারবেন এবং পৃথক শ্রেণীর ডেটা সদস্যদের বেশ কয়েকটি রিটার্ন মান সংরক্ষণ করতে পারেন। এটি ওওপি ধারণাটির নমনীয়তাকে আন্ডারস্কোর করে।
রোল্যান্ড

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;
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.