উত্তর:
সি ++ 14 এ আমরা তথাকথিত জেনারালাইজড লাম্বদা ক্যাপচারটি করব । এটি সরানো ক্যাপচার সক্ষম করে। নীচে সি ++ 14 এ আইনী কোড হবে:
using namespace std;
// a unique_ptr is move-only
auto u = make_unique<some_type>( some, parameters );
// move the unique_ptr into the lambda
go.run( [ u{move(u)} ] { do_something_with( u ); } );
তবে এটি এই অর্থে আরও সাধারণ যে ক্যাপচারেড ভেরিয়েবলগুলি এরকম কিছু দিয়ে শুরু করা যেতে পারে:
auto lambda = [value = 0] mutable { return ++value; };
সি ++ 11 এ এটি এখনও সম্ভব নয়, তবে এমন কিছু কৌশল দ্বারা যা সহায়ক ধরণের সাথে জড়িত। ভাগ্যক্রমে, কলঙ্ক 3.4 সংকলক ইতিমধ্যে এই দুর্দান্ত বৈশিষ্ট্যটি প্রয়োগ করে। সংকলক সাম্প্রতিক প্রকাশের গতি থাকলে ডিসেম্বর 2013 বা জানুয়ারী 2014 প্রকাশিত হবে যদি রাখা হয় হবে।
আপডেট: ঝনঝন 3.4 কম্পাইলার বলেন বৈশিষ্ট্য সঙ্গে 6 জানুয়ারী 2014 মুক্তি পায়।
এখানে একটি সহায়ক ফাংশনের একটি বাস্তবায়ন make_rref
যা কৃত্রিম পদক্ষেপ ক্যাপচারে সহায়তা করে
#include <cassert>
#include <memory>
#include <utility>
template <typename T>
struct rref_impl
{
rref_impl() = delete;
rref_impl( T && x ) : x{std::move(x)} {}
rref_impl( rref_impl & other )
: x{std::move(other.x)}, isCopied{true}
{
assert( other.isCopied == false );
}
rref_impl( rref_impl && other )
: x{std::move(other.x)}, isCopied{std::move(other.isCopied)}
{
}
rref_impl & operator=( rref_impl other ) = delete;
T && move()
{
return std::move(x);
}
private:
T x;
bool isCopied = false;
};
template<typename T> rref_impl<T> make_rref( T && x )
{
return rref_impl<T>{ std::move(x) };
}
এবং এখানে ফাংশনটির জন্য একটি পরীক্ষার কেস যা আমার জিসিসি ৪..3.৩ এ সাফল্যের সাথে চলে ran
int main()
{
std::unique_ptr<int> p{new int(0)};
auto rref = make_rref( std::move(p) );
auto lambda =
[rref]() mutable -> std::unique_ptr<int> { return rref.move(); };
assert( lambda() );
assert( !lambda() );
}
এখানে অপূর্ণতা হ'ল এটি lambda
অনুলিপিযোগ্য এবং যখন অনুলিপিটির অনুলিপিটি অনুলিপি করেছেন ofrref_impl
ব্যর্থ হয় রন্টটাইম বাগের দিকে নিয়ে যায়। নিম্নলিখিতটি আরও ভাল এবং আরও সাধারণ সমাধান হতে পারে কারণ সংকলকটি ত্রুটিটি ধরবে catch
সাধারণভাবে ল্যাম্বডা ক্যাপচার কীভাবে প্রয়োগ করা যায় সে সম্পর্কে এখানে আরও একটি ধারণা's ফাংশনটির ব্যবহার capture()
(যার প্রয়োগটি আরও নীচে পাওয়া যায়):
#include <cassert>
#include <memory>
int main()
{
std::unique_ptr<int> p{new int(0)};
auto lambda = capture( std::move(p),
[]( std::unique_ptr<int> & p ) { return std::move(p); } );
assert( lambda() );
assert( !lambda() );
}
এখানে lambda
একটি ফান্টর অবজেক্ট (প্রায় একটি বাস্তব ল্যাম্বদা) যা এটি std::move(p)
পাস করার সাথে সাথে ক্যাপচার করেছে capture()
। দ্বিতীয় আর্গুমেন্টটি capture
একটি ল্যাম্বডা যা ক্যাপচারড ভেরিয়েবলটিকে আর্গুমেন্ট হিসাবে গ্রহণ করে। যখন lambda
কোনও ফাংশন অবজেক্ট হিসাবে ব্যবহৃত হয়, তারপরে যাবতীয় সমস্ত যুক্তি আটকানো ভেরিয়েবলের পরে আর্গুমেন্ট হিসাবে অভ্যন্তরীণ ল্যাম্বডায় ফরোয়ার্ড করা হবে। (আমাদের ক্ষেত্রে এগিয়ে যাওয়ার আর কোনও যুক্তি নেই)। মূলত, আগের সমাধানের মতোই ঘটে। এখানে কীভাবে capture
প্রয়োগ করা হয়:
#include <utility>
template <typename T, typename F>
class capture_impl
{
T x;
F f;
public:
capture_impl( T && x, F && f )
: x{std::forward<T>(x)}, f{std::forward<F>(f)}
{}
template <typename ...Ts> auto operator()( Ts&&...args )
-> decltype(f( x, std::forward<Ts>(args)... ))
{
return f( x, std::forward<Ts>(args)... );
}
template <typename ...Ts> auto operator()( Ts&&...args ) const
-> decltype(f( x, std::forward<Ts>(args)... ))
{
return f( x, std::forward<Ts>(args)... );
}
};
template <typename T, typename F>
capture_impl<T,F> capture( T && x, F && f )
{
return capture_impl<T,F>(
std::forward<T>(x), std::forward<F>(f) );
}
এই দ্বিতীয় সমাধানটিও ক্লিনার, কারণ এটি ল্যাম্বডা অনুলিপি করে অক্ষম করে, যদি বন্দী প্রকারটি অনুলিপিযোগ্য না হয়। প্রথম সমাধানে যা কেবল রান টাইমে একটি সাথে পরীক্ষা করা যায় assert()
।
moveCapture
যুক্তি হিসাবে পাস করার জন্য একটি মোড়ক ব্যবহার করুন (এই পদ্ধতিটি উপরে এবং ক্যাপনপ্রোটোতে ব্যবহৃত হয়েছে প্রোটোবফগুলির স্রষ্টার দ্বারা নির্মিত একটি লাইব্রেরি) বা কেবল এটির সমর্থন করুন এমন সংকলক প্রয়োজন বলে গ্রহণ করুন: পি
আপনি std::bind
এটি ক্যাপচার করতেও ব্যবহার করতে পারেন unique_ptr
:
std::function<void()> f = std::bind(
[] (std::unique_ptr<int>& p) { *p=4; },
std::move(myPointer)
);
unique_ptr
মূল্যের রেফারেন্স একটিতে আবদ্ধ হতে পারে না int *
।
myPointer
এই ক্ষেত্রে) in সুতরাং উপরের কোডটি ভিএস ২০১৩ সালে সংকলন করে না। এটি জিসিসিতে ৪.৮ হলেও জরিমানা।
আপনি যা ব্যবহার করতে চান তার বেশিরভাগটি অর্জন করতে পারেন 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
এটি অদৃশ্য হওয়ার জন্য আংশিক অ্যাপ্লিকেশন ব্যবহার করি। নোট করুন যে ল্যাম্বডা এটি রেফারেন্সের দ্বারা গ্রহণ করে , কারণ এটি আসলে বাঁধার বস্তুতে সঞ্চিত রয়েছে। আমি আসল অস্থাবর বস্তুতে লেখার কোডও যুক্ত করেছিলাম , কারণ এটি এমন কিছু যা আপনি করতে চান।
সি ++ এ, আপনি এই কোড সহ একই প্রান্তগুলি অর্জন করতে সাধারণ ল্যাম্বডা ক্যাপচার ব্যবহার করতে পারেন:
std::unique_ptr<int> myPointer(new int{42});
auto lambda = [myPointerCapture = std::move(myPointer)]() mutable {
*myPointerCapture = 56;
myPointerCapture.reset(new int{237});
};
তবে এই কোডটি আপনার কাছে সি ++ 11 এর মাধ্যমে নেই এমন কিছুই কিনে না std::bind
। (কিছু পরিস্থিতি রয়েছে যেখানে ল্যাম্বা ক্যাপচারকে আরও শক্তিশালী করা হয় তবে এই ক্ষেত্রে নয় not)
এখন কেবল একটি সমস্যা আছে; আপনি এই ফাংশনটি একটিতে রাখতে চেয়েছিলেন std::function
, তবে সেই শ্রেণীর জন্য ফাংশনটি কপিরকমস্ট্রাক্টিয়েবল হওয়া দরকার , তবে তা নয়, এটি কেবল মুভকনস্ট্রেকটিভেবল কারণ এটি এমন একটি সংরক্ষণাগার সংরক্ষণ করে std::unique_ptr
যা কপিরিকস্ট্রাক্টেবল নয় ।
আপনার মোড়কের ক্লাস এবং ইন্ডিয়ারেশনের অন্য স্তরের সাথে ইস্যুটি নিয়ে কাজ করার জন্য, তবে সম্ভবত আপনার এগুলির প্রয়োজন হবে না std::function
। আপনার প্রয়োজনের উপর নির্ভর করে আপনি ব্যবহার করতে সক্ষম হতে পারেন std::packaged_task
; এটি একই কাজটি করত std::function
তবে এটির ফাংশনটি অনুলিপিযোগ্য হতে পারে না, কেবল চলমান (একইভাবে std::packaged_task
কেবল চলমান)। নেতিবাচক দিকটি এটি যেহেতু এটি std :: ভবিষ্যতের সাথে একত্রে ব্যবহৃত হবে, আপনি কেবল একবার এটি কল করতে পারেন।
এখানে একটি ছোট প্রোগ্রাম যা এই সমস্ত ধারণাটি দেখায়।
#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
সঠিকভাবে কাজ করছে showing এছাড়াও আপনি ফাংশন নিজেই প্রায় সরাতে আমরা যখন এটা লুকিয়ে মোড়কের আমরা ভোজন দেখতে 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
এটি স্ট্যাকের ভিতরে রয়েছে ।
আশাকরি এটা সাহায্য করবে!
দেরীতে, তবে কিছু লোক (আমাকে সহ) এখনও সি ++ 11 এ আটকে রয়েছে:
সত্যি কথা বলতে কি আমি পোস্ট করা সমাধানের কোনওটিই পছন্দ করি না। আমি নিশ্চিত যে তারা কাজ করবে, তবে তাদের জন্য প্রচুর অতিরিক্ত স্টাফ এবং / বা ক্রিপ্টিক্যাল std::bind
সিনট্যাক্স প্রয়োজন ... এবং আমি মনে করি না যে এই ধরনের অস্থায়ী সমাধানের জন্য প্রচেষ্টাটি মূল্যবান যেটি সি ++> এ আপগ্রেড করার পরে যেভাবেই রিফেক্ট হবে act = 14. সুতরাং আমি মনে করি সি ++ 11 এর জন্য পুরোপুরি ক্যাপচার করা এড়ানো সবচেয়ে ভাল সমাধান।
সাধারণত সহজ এবং সর্বোত্তম পঠনযোগ্য সমাধানটি হ'ল ব্যবহার করা std::shared_ptr
, যা অনুলিপিযোগ্য এবং তাই সরানো সম্পূর্ণরূপে এড়ানো যায়। ডাউনসাইড হ'ল, এটি সামান্য কম দক্ষ, তবে অনেক ক্ষেত্রে দক্ষতা এত গুরুত্বপূর্ণ নয়।
// myPointer could be a parameter or something
std::unique_ptr<int> myPointer(new int);
// convert/move the unique ptr into a shared ptr
std::shared_ptr<int> mySharedPointer( std::move(myPointer) );
std::function<void(void)> = [mySharedPointer](){
*mySharedPointer = 4;
};
// at end of scope the original mySharedPointer is destroyed,
// but the copy still lives in the lambda capture.
।
খুব বিরল ক্ষেত্রে যদি ঘটে থাকে তবে এটি move
পয়েন্টারের কাছে সত্যিই বাধ্যতামূলক (যেমন আপনি দীর্ঘ মুছে ফেলার সময়কালের কারণে পৃথক থ্রেডে স্পষ্টভাবে একটি পয়েন্টার মুছতে চান, বা পারফরম্যান্স একেবারে গুরুত্বপূর্ণ), এটি এখনও এমন একমাত্র ক্ষেত্রে যেখানে আমি এখনও ব্যবহার করি সি ++ 11 এ কাঁচা পয়েন্টার। এগুলি অবশ্যই অনুলিপিযোগ্য।
সাধারণত আমি এইগুলির সাথে এই বিরল ঘটনাগুলি চিহ্নিত করি //FIXME:
একবারে সি ++ 14 এ আপগ্রেড হওয়ার পরে এটি রিফেক্টর হয় তা নিশ্চিত করার জন্য কেসগুলিকে ।
// myPointer could be a parameter or something
std::unique_ptr<int> myPointer(new int);
//FIXME:c++11 upgrade to new move capture on c++>=14
// "move" the pointer into a raw pointer
int* myRawPointer = myPointer.release();
// capture the raw pointer as a copy.
std::function<void(void)> = [myRawPointer](){
std::unique_ptr<int> capturedPointer(myRawPointer);
*capturedPointer = 4;
};
// ensure that the pointer's value is not accessible anymore after capturing
myRawPointer = nullptr;
হ্যাঁ, কাঁচা পয়েন্টারগুলি এই দিনগুলিতে (এবং কারণ ছাড়াই নয়) খুব সুন্দর হয়েছে, তবে আমি সত্যিই মনে করি এই বিরল (এবং অস্থায়ী!) ক্ষেত্রে তারা সেরা সমাধান solution
আমি এই উত্তরগুলি খুঁজছিলাম, কিন্তু আমি পড়তে এবং বুঝতে শক্তভাবে বাঁধাই পেয়েছি। সুতরাং আমি যা করেছি তা হ'ল একটি ক্লাস যা পরিবর্তে অনুলিপিটিতে সরে গেল moved এইভাবে, এটি কী করছে তা স্পষ্ট।
#include <iostream>
#include <memory>
#include <utility>
#include <type_traits>
#include <functional>
namespace detail
{
enum selection_enabler { enabled };
}
#define ENABLE_IF(...) std::enable_if_t<(__VA_ARGS__), ::detail::selection_enabler> \
= ::detail::enabled
// This allows forwarding an object using the copy constructor
template <typename T>
struct move_with_copy_ctor
{
// forwarding constructor
template <typename T2
// Disable constructor for it's own type, since it would
// conflict with the copy constructor.
, ENABLE_IF(
!std::is_same<std::remove_reference_t<T2>, move_with_copy_ctor>::value
)
>
move_with_copy_ctor(T2&& object)
: wrapped_object(std::forward<T2>(object))
{
}
// move object to wrapped_object
move_with_copy_ctor(T&& object)
: wrapped_object(std::move(object))
{
}
// Copy constructor being used as move constructor.
move_with_copy_ctor(move_with_copy_ctor const& object)
{
std::swap(wrapped_object, const_cast<move_with_copy_ctor&>(object).wrapped_object);
}
// access to wrapped object
T& operator()() { return wrapped_object; }
private:
T wrapped_object;
};
template <typename T>
move_with_copy_ctor<T> make_movable(T&& object)
{
return{ std::forward<T>(object) };
}
auto fn1()
{
std::unique_ptr<int, std::function<void(int*)>> x(new int(1)
, [](int * x)
{
std::cout << "Destroying " << x << std::endl;
delete x;
});
return [y = make_movable(std::move(x))]() mutable {
std::cout << "value: " << *y() << std::endl;
return;
};
}
int main()
{
{
auto x = fn1();
x();
std::cout << "object still not deleted\n";
x();
}
std::cout << "object was deleted\n";
}
move_with_copy_ctor
শ্রেণী এবং এটা সাহায্যকারী ফাংশন make_movable()
কোন অস্থাবর কিন্তু copyable বস্তুর সঙ্গে কাজ করবে। মোড়ানো জিনিসটিতে অ্যাক্সেস পেতে, ব্যবহার করুন operator()()
।
প্রত্যাশিত আউটপুট:
মান: 1 বস্তু এখনও মুছে ফেলা হয় না মান: 1 000000DFDD172280 ধ্বংস হচ্ছে অবজেক্ট মুছে ফেলা হয়েছে
ঠিক আছে, পয়েন্টার ঠিকানা পৃথক হতে পারে। ;)