ল্যাম্বডায় ক্যাপচার সরান


157

আমি কীভাবে একটি সি ++ 11 ল্যাম্বডায় চলুন (রুল্যু রেফারেন্স হিসাবে পরিচিত) ক্যাপচার করব?

আমি এরকম কিছু লেখার চেষ্টা করছি:

std::unique_ptr<int> myPointer(new int);

std::function<void(void)> example = [std::move(myPointer)]{
   *myPointer = 4;
};

উত্তর:


163

সি ++ 14 এ জেনারালাইজড ল্যাম্বদা ক্যাপচার

সি ++ 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 মুক্তি পায়।

সরানো ক্যাপচার জন্য একটি workaround

এখানে একটি সহায়ক ফাংশনের একটি বাস্তবায়ন 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

সি ++ 11 এ জেনারেলাইজড ল্যাম্বডা ক্যাপচারটি এমুলেট করা

সাধারণভাবে ল্যাম্বডা ক্যাপচার কীভাবে প্রয়োগ করা যায় সে সম্পর্কে এখানে আরও একটি ধারণা'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()


আমি দীর্ঘকাল এটি জি ++ - ৪.৮-স্টাড = সি ++ ১১ দিয়ে ব্যবহার করছি এবং আমি ভেবেছিলাম এটি একটি সি ++ ১১ বৈশিষ্ট্য। এখন আমি এটি ব্যবহার করে ব্যবহার করছি এবং হঠাৎ বুঝতে পারলাম এটি একটি সি ++ 14 বৈশিষ্ট্য ... আমার কী করা উচিত !!
আরএনএমএস

@ আরএনএমএস আপনি কোন বৈশিষ্ট্যটি বোঝাতে চাইছেন? লম্বা জেনারালাইজড ক্যাপচার?
রাল্ফ ট্যান্ডেটস্কি

@ রাল্ফট্যান্ডেটজকি আমার মনে হয়, আমি কেবল এটি যাচাই করেছিলাম এবং এক্সকোডের সাথে বান্ডিলযুক্ত ঝনঝন সংস্করণটিও এটি সমর্থন করে বলে মনে হচ্ছে! এটি একটি সতর্কতা দেয় যে এটি একটি C ++ 1y এক্সটেনশন তবে এটি কার্যকর হয়।
ক্রিস্টোফার

@ আরএনএমএস হয় এগুলি moveCaptureযুক্তি হিসাবে পাস করার জন্য একটি মোড়ক ব্যবহার করুন (এই পদ্ধতিটি উপরে এবং ক্যাপনপ্রোটোতে ব্যবহৃত হয়েছে প্রোটোবফগুলির স্রষ্টার দ্বারা নির্মিত একটি লাইব্রেরি) বা কেবল এটির সমর্থন করুন এমন সংকলক প্রয়োজন বলে গ্রহণ করুন: পি
ক্রিস্টোফার তারকুইনি

9
না, এটি আসলে একই জিনিস নয়। উদাহরণ: আপনি একটি ল্যাম্বডা দিয়ে একটি থ্রেড ছড়িয়ে দিতে চান যা অনন্য পয়েন্টারটিকে মুভ করে। স্প্যানিং ফাংশনটি সম্ভবত ফিরে আসতে পারে এবং ফিঙ্ক্টর কার্যকর হওয়ার আগে ইউনিক_পিটার সুযোগের বাইরে চলে যায়। অতএব, আপনার কাছে একটি অনন্য_প্টারের কাছে ঝোঁক রেফারেন্স রয়েছে। অপরিবর্তিত আচরণ-স্থল স্বাগতম।
রাল্ফ ট্যান্ডেটস্কি

76

আপনি std::bindএটি ক্যাপচার করতেও ব্যবহার করতে পারেন unique_ptr:

std::function<void()> f = std::bind(
                              [] (std::unique_ptr<int>& p) { *p=4; },
                              std::move(myPointer)
                          );

2
এই পোস্ট করার জন্য আপনাকে ধন্যবাদ!
মিমোকনি

4
কোডটি কম্পাইল করলে আপনি কি পরীক্ষা করে দেখেছেন? এটি আমার কাছে তেমন লাগে না, যেহেতু প্রথমত ভেরিয়েবলের নামটি অনুপস্থিত এবং দ্বিতীয়ত কোনও unique_ptrমূল্যের রেফারেন্স একটিতে আবদ্ধ হতে পারে না int *
র‌্যাল্ফ ট্যান্ডেটস্কি

7
নোট করুন যে ভিজ্যুয়াল স্টুডিও ২০১৩-তে, একটি স্টাডি :: বাইন্ডকে একটি এসডিডি :: ফাংশনে রূপান্তরিত করার ফলে এটি এখনও সমস্ত বাউন্ড ভেরিয়েবলগুলি অনুলিপি করে ( myPointerএই ক্ষেত্রে) in সুতরাং উপরের কোডটি ভিএস ২০১৩ সালে সংকলন করে না। এটি জিসিসিতে ৪.৮ হলেও জরিমানা।
অ্যালান

22

আপনি যা ব্যবহার করতে চান তার বেশিরভাগটি অর্জন করতে পারেন 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এটি স্ট্যাকের ভিতরে রয়েছে ।

আশাকরি এটা সাহায্য করবে!


4

দেরীতে, তবে কিছু লোক (আমাকে সহ) এখনও সি ++ 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


ধন্যবাদ, সি ++ 14 ব্যবহার করা এবং অন্যান্য সমাধানগুলির মধ্যে ভাল ছিল না। আমার দিন বাঁচা!
Yoav স্টার্নবার্গ

1

আমি এই উত্তরগুলি খুঁজছিলাম, কিন্তু আমি পড়তে এবং বুঝতে শক্তভাবে বাঁধাই পেয়েছি। সুতরাং আমি যা করেছি তা হ'ল একটি ক্লাস যা পরিবর্তে অনুলিপিটিতে সরে গেল 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 ধ্বংস হচ্ছে
অবজেক্ট মুছে ফেলা হয়েছে

ঠিক আছে, পয়েন্টার ঠিকানা পৃথক হতে পারে। ;)

Demo


1

এটি gcc4.8 এ কাজ করে বলে মনে হচ্ছে

#include <memory>
#include <iostream>

struct Foo {};

void bar(std::unique_ptr<Foo> p) {
    std::cout << "bar\n";
}

int main() {
    std::unique_ptr<Foo> p(new Foo);
    auto f = [ptr = std::move(p)]() mutable {
        bar(std::move(ptr));
    };
    f();
    return 0;
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.