कई मापदंडों को वापस करने के तरीकों का एक गुच्छा है। मैं बहकने वाला हूँ।
संदर्भ मापदंडों का उपयोग करें:
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 ++ के हर संस्करण में, और अंदर काम करता है c ++ 17 यह भी अनुमति देता है:
auto&&[result, other_result]=foo();
शून्य लागत पर। पैरामीटर भी गारंटीकृत elision के लिए धन्यवाद स्थानांतरित नहीं किया जा सकता है।
हम वापस आ सकते हैं std::tuple:
std::tuple<int, int> foo();
जिसका नकारात्मक पहलू यह है कि मापदंडों का नाम नहीं है। यह अनुमति देता हैc ++ 17:
auto&&[result, other_result]=foo();
भी। इसके पहलेc ++ 17 हम इसके बजाय कर सकते हैं:
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औरc ++ 20coroutines, आप 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परिणामों को समझना शुरू करते हैं।