आप std::bind
इस तरह से जो भी उपयोग करना चाहते हैं , उसे प्राप्त कर सकते हैं :
std::unique_ptr<int> myPointer(new int{42});
auto lambda = std::bind([](std::unique_ptr<int>& myPointerArg){
*myPointerArg = 4;
myPointerArg.reset(new int{237});
}, std::move(myPointer));
यहाँ ट्रिक यह है कि कैप्चर लिस्ट में आपकी मूव-ओनली ऑब्जेक्ट को कैप्चर करने के बजाय, हम इसे एक तर्क बनाते हैं और फिर std::bind
इसे गायब करने के लिए आंशिक एप्लिकेशन का उपयोग करते हैं । ध्यान दें कि लैम्ब्डा इसे संदर्भ द्वारा लेता है , क्योंकि यह वास्तव में बाइंड ऑब्जेक्ट में संग्रहीत है। मैंने वह कोड भी जोड़ा है जो वास्तविक चल वस्तु पर लिखता है, क्योंकि वह कुछ ऐसा है जो आप करना चाहते हैं।
C ++ 14 में, आप इस कोड के साथ समान छोरों को प्राप्त करने के लिए सामान्यीकृत लैम्ब्डा कैप्चर का उपयोग कर सकते हैं:
std::unique_ptr<int> myPointer(new int{42});
auto lambda = [myPointerCapture = std::move(myPointer)]() mutable {
*myPointerCapture = 56;
myPointerCapture.reset(new int{237});
};
लेकिन यह कोड आपको C ++ 11 के माध्यम से कुछ भी नहीं खरीदता है std::bind
। (कुछ परिस्थितियां हैं जहां सामान्यीकृत लैम्ब्डा कैप्चर अधिक शक्तिशाली है, लेकिन इस मामले में नहीं।)
अब सिर्फ एक समस्या है; आप इस फ़ंक्शन को एक में रखना चाहते थे std::function
, लेकिन उस वर्ग के लिए आवश्यक है कि फ़ंक्शन CopyConstructible हो , लेकिन ऐसा नहीं है, यह केवल MoveConstructible है क्योंकि यह एक ऐसा स्टोर है std::unique_ptr
जो CopyConstructible नहीं है ।
आप रैपर वर्ग और अप्रत्यक्ष स्तर के दूसरे स्तर पर इस मुद्दे पर काम कर सकते हैं, लेकिन शायद आपको इसकी बिल्कुल भी जरूरत नहीं है std::function
। अपनी आवश्यकताओं के आधार पर, आप उपयोग करने में सक्षम हो सकते हैं std::packaged_task
; यह उसी तरह काम करेगा std::function
, लेकिन इसके लिए फ़ंक्शन को प्रतिलिपि योग्य होने की आवश्यकता नहीं है, केवल चल (इसी तरह, std::packaged_task
केवल चल) है। नकारात्मक पक्ष यह है कि क्योंकि यह एसटीडी :: भविष्य के साथ संयोजन के रूप में उपयोग करने का इरादा है, आप इसे केवल एक बार कॉल कर सकते हैं।
यहाँ एक छोटा कार्यक्रम है जो इन सभी अवधारणाओं को दर्शाता है।
#include <functional> // for std::bind
#include <memory> // for std::unique_ptr
#include <utility> // for std::move
#include <future> // for std::packaged_task
#include <iostream> // printing
#include <type_traits> // for std::result_of
#include <cstddef>
void showPtr(const char* name, const std::unique_ptr<size_t>& ptr)
{
std::cout << "- &" << name << " = " << &ptr << ", " << name << ".get() = "
<< ptr.get();
if (ptr)
std::cout << ", *" << name << " = " << *ptr;
std::cout << std::endl;
}
// If you must use std::function, but your function is MoveConstructable
// but not CopyConstructable, you can wrap it in a shared pointer.
template <typename F>
class shared_function : public std::shared_ptr<F> {
public:
using std::shared_ptr<F>::shared_ptr;
template <typename ...Args>
auto operator()(Args&&...args) const
-> typename std::result_of<F(Args...)>::type
{
return (*(this->get()))(std::forward<Args>(args)...);
}
};
template <typename F>
shared_function<F> make_shared_fn(F&& f)
{
return shared_function<F>{
new typename std::remove_reference<F>::type{std::forward<F>(f)}};
}
int main()
{
std::unique_ptr<size_t> myPointer(new size_t{42});
showPtr("myPointer", myPointer);
std::cout << "Creating lambda\n";
#if __cplusplus == 201103L // C++ 11
// Use std::bind
auto lambda = std::bind([](std::unique_ptr<size_t>& myPointerArg){
showPtr("myPointerArg", myPointerArg);
*myPointerArg *= 56; // Reads our movable thing
showPtr("myPointerArg", myPointerArg);
myPointerArg.reset(new size_t{*myPointerArg * 237}); // Writes it
showPtr("myPointerArg", myPointerArg);
}, std::move(myPointer));
#elif __cplusplus > 201103L // C++14
// Use generalized capture
auto lambda = [myPointerCapture = std::move(myPointer)]() mutable {
showPtr("myPointerCapture", myPointerCapture);
*myPointerCapture *= 56;
showPtr("myPointerCapture", myPointerCapture);
myPointerCapture.reset(new size_t{*myPointerCapture * 237});
showPtr("myPointerCapture", myPointerCapture);
};
#else
#error We need C++11
#endif
showPtr("myPointer", myPointer);
std::cout << "#1: lambda()\n";
lambda();
std::cout << "#2: lambda()\n";
lambda();
std::cout << "#3: lambda()\n";
lambda();
#if ONLY_NEED_TO_CALL_ONCE
// In some situations, std::packaged_task is an alternative to
// std::function, e.g., if you only plan to call it once. Otherwise
// you need to write your own wrapper to handle move-only function.
std::cout << "Moving to std::packaged_task\n";
std::packaged_task<void()> f{std::move(lambda)};
std::cout << "#4: f()\n";
f();
#else
// Otherwise, we need to turn our move-only function into one that can
// be copied freely. There is no guarantee that it'll only be copied
// once, so we resort to using a shared pointer.
std::cout << "Moving to std::function\n";
std::function<void()> f{make_shared_fn(std::move(lambda))};
std::cout << "#4: f()\n";
f();
std::cout << "#5: f()\n";
f();
std::cout << "#6: f()\n";
f();
#endif
}
मैंने कॉलिरु पर एक उपरोक्त कार्यक्रम रखा है , ताकि आप कोड के साथ चला सकें और खेल सकें।
यहां जानिए कुछ खास आउटपुट ...
- &myPointer = 0xbfffe5c0, myPointer.get() = 0x7ae3cfd0, *myPointer = 42
Creating lambda
- &myPointer = 0xbfffe5c0, myPointer.get() = 0x0
#1: lambda()
- &myPointerArg = 0xbfffe5b4, myPointerArg.get() = 0x7ae3cfd0, *myPointerArg = 42
- &myPointerArg = 0xbfffe5b4, myPointerArg.get() = 0x7ae3cfd0, *myPointerArg = 2352
- &myPointerArg = 0xbfffe5b4, myPointerArg.get() = 0x7ae3cfe0, *myPointerArg = 557424
#2: lambda()
- &myPointerArg = 0xbfffe5b4, myPointerArg.get() = 0x7ae3cfe0, *myPointerArg = 557424
- &myPointerArg = 0xbfffe5b4, myPointerArg.get() = 0x7ae3cfe0, *myPointerArg = 31215744
- &myPointerArg = 0xbfffe5b4, myPointerArg.get() = 0x7ae3cfd0, *myPointerArg = 3103164032
#3: lambda()
- &myPointerArg = 0xbfffe5b4, myPointerArg.get() = 0x7ae3cfd0, *myPointerArg = 3103164032
- &myPointerArg = 0xbfffe5b4, myPointerArg.get() = 0x7ae3cfd0, *myPointerArg = 1978493952
- &myPointerArg = 0xbfffe5b4, myPointerArg.get() = 0x7ae3cfe0, *myPointerArg = 751631360
Moving to std::function
#4: f()
- &myPointerArg = 0x7ae3cfd4, myPointerArg.get() = 0x7ae3cfe0, *myPointerArg = 751631360
- &myPointerArg = 0x7ae3cfd4, myPointerArg.get() = 0x7ae3cfe0, *myPointerArg = 3436650496
- &myPointerArg = 0x7ae3cfd4, myPointerArg.get() = 0x7ae3d000, *myPointerArg = 2737348608
#5: f()
- &myPointerArg = 0x7ae3cfd4, myPointerArg.get() = 0x7ae3d000, *myPointerArg = 2737348608
- &myPointerArg = 0x7ae3cfd4, myPointerArg.get() = 0x7ae3d000, *myPointerArg = 2967666688
- &myPointerArg = 0x7ae3cfd4, myPointerArg.get() = 0x7ae3cfe0, *myPointerArg = 3257335808
#6: f()
- &myPointerArg = 0x7ae3cfd4, myPointerArg.get() = 0x7ae3cfe0, *myPointerArg = 3257335808
- &myPointerArg = 0x7ae3cfd4, myPointerArg.get() = 0x7ae3cfe0, *myPointerArg = 2022178816
- &myPointerArg = 0x7ae3cfd4, myPointerArg.get() = 0x7ae3d000, *myPointerArg = 2515009536
आपको यह देखने के लिए मिलता है कि ढेर के स्थान पुन: उपयोग किए जा रहे हैं, यह दर्शाता है कि यह std::unique_ptr
ठीक से काम कर रहा है। आप यह भी देखते हैं कि जब हम किसी रैपर में खाना खिलाते हैं तो फंक्शन खुद ही घूम जाता है std::function
।
यदि हम उपयोग करने के लिए स्विच करते हैं std::packaged_task
, तो यह अंतिम भाग बन जाता है
Moving to std::packaged_task
#4: f()
- &myPointerArg = 0xbfffe590, myPointerArg.get() = 0x7ae3cfe0, *myPointerArg = 751631360
- &myPointerArg = 0xbfffe590, myPointerArg.get() = 0x7ae3cfe0, *myPointerArg = 3436650496
- &myPointerArg = 0xbfffe590, myPointerArg.get() = 0x7ae3d000, *myPointerArg = 2737348608
इसलिए हम देखते हैं कि फ़ंक्शन को स्थानांतरित कर दिया गया है, लेकिन ढेर पर स्थानांतरित होने के बजाय, यह std::packaged_task
स्टैक पर है।
उम्मीद है की यह मदद करेगा!