c++ - স্যাম্যান্টিক্স কি সরানো হয়?




c++-faq c++11 (8)

আমার প্রথম উত্তরটি স্যাম্যান্টিক্সকে সরানোর জন্য অত্যন্ত সরলীকৃত ভূমিকা ছিল এবং এটি সহজ রাখার উদ্দেশ্যে অনেকগুলি বিবরণ বাদ দেওয়া হয়েছিল। যাইহোক, semantics সরাতে আরো অনেক কিছু আছে, এবং আমি ভাঙ্গা পূরণের একটি দ্বিতীয় উত্তর জন্য সময় ছিল। প্রথম উত্তরটি পুরোপুরি পুরাতন, এবং একে একে একে সম্পূর্ণরূপে ভিন্ন পাঠ্যের সাথে প্রতিস্থাপন করার অধিকার মনে হয়নি। আমি এখনও এটি একটি প্রথম ভূমিকা হিসাবে ভাল কাজ করে। কিন্তু আপনি গভীর খনন করতে চান, পড়তে :)

স্টিফেন টি। লাভভেজ সময় মূল্যবান মতামত প্রদান করেন। অনেক ধন্যবাদ, স্টিফান!

ভূমিকা

স্যাম্যান্টিক্স কিছু বস্তুর বহিরাগত সংস্থানের মালিকানা নিতে কিছু নির্দিষ্ট অবস্থার অধীনে একটি বস্তুর অনুমোদন দেয়। এটি দুটি উপায়ে গুরুত্বপূর্ণ:

  1. সস্তা প্যাচসমূহ মধ্যে ব্যয়বহুল কপি চালু। একটি উদাহরণ জন্য আমার প্রথম উত্তর দেখুন। উল্লেখ্য, যদি কোনও বস্তু কমপক্ষে একটি বহিরাগত সংস্থান পরিচালনা করে না (সরাসরি, অথবা তার সদস্য বস্তুর মাধ্যমে পরোক্ষভাবে), সেমেটিক্স অনুলিপি স্যাম্যান্টিকসের উপর কোনও সুবিধা প্রদান করবে না। যে ক্ষেত্রে, একটি বস্তু অনুলিপি করা এবং একটি বস্তু সরানো মানে একই জিনিস:

    class cannot_benefit_from_move_semantics
    {
        int a;        // moving an int means copying an int
        float b;      // moving a float means copying a float
        double c;     // moving a double means copying a double
        char d[64];   // moving a char array means copying a char array
    
        // ...
    };
  2. নিরাপদ বাস্তবায়ন "সরানো শুধুমাত্র" ধরনের; যে, কোন ধরনের অনুলিপি জ্ঞান করে না, কিন্তু চলন্ত না। উদাহরণগুলি তালা, ফাইল হ্যান্ডলগুলি এবং অনন্য মালিকানা শব্দার্থবিদ্যা সহ স্মার্ট পয়েন্টার অন্তর্ভুক্ত। দ্রষ্টব্য: এই উত্তরটি std::auto_ptr , একটি std::auto_ptr সি ++ 98 স্ট্যান্ডার্ড লাইব্রেরি টেম্পলেট নিয়ে আলোচনা করে, যা সি ++ 11 এ std::unique_ptr দ্বারা প্রতিস্থাপিত হয়েছিল। ইন্টারমিডিয়েট সি ++ প্রোগ্রামার সম্ভবত কমপক্ষে কিছুটা পরিচিত std::auto_ptr এবং এটি "চলমান std::auto_ptr " এর কারণে এটি প্রদর্শিত হয়, এটি C ++ 11 এ সরানো স্যাম্যান্টিক্স নিয়ে আলোচনা করার জন্য একটি ভাল std::auto_ptr দিক বলে মনে হয়। YMMV।

একটি পদক্ষেপ কি?

সি ++ 98 স্ট্যান্ডার্ড লাইব্রেরিটি std::auto_ptr<T> নামক অনন্য মালিকানা std::auto_ptr<T> সাথে একটি স্মার্ট পয়েন্টার সরবরাহ করে। যদি আপনি auto_ptr সাথে অপরিচিত না হন, তবে এর উদ্দেশ্য গ্যারান্টি দেওয়া যে একটি গতিশীলভাবে বরাদ্দ করা বস্তুটি সর্বদা ছেড়ে দেওয়া হয়, ব্যতিক্রমগুলির মুখেও:

{
    std::auto_ptr<Shape> a(new Triangle);
    // ...
    // arbitrary code, could throw exceptions
    // ...
}   // <--- when a goes out of scope, the triangle is deleted automatically

auto_ptr সম্পর্কে অস্বাভাবিক জিনিস এটির "অনুলিপি" আচরণ:

auto_ptr<Shape> a(new Triangle);

      +---------------+
      | triangle data |
      +---------------+
        ^
        |
        |
        |
  +-----|---+
  |   +-|-+ |
a | p | | | |
  |   +---+ |
  +---------+

auto_ptr<Shape> b(a);

      +---------------+
      | triangle data |
      +---------------+
        ^
        |
        +----------------------+
                               |
  +---------+            +-----|---+
  |   +---+ |            |   +-|-+ |
a | p |   | |          b | p | | | |
  |   +---+ |            |   +---+ |
  +---------+            +---------+

দ্রষ্টব্য কীভাবে b এর সূচনাটি ত্রিভুজকে অনুলিপি করে না , বরং ত্রিভুজটির মালিকানাটি a থেকে b স্থানান্তরিত করে। আমরা বলি " a b স্থানান্তরিত হয় " বা "ত্রিভুজটি b থেকে b তে সরানো হয়"। এই বিভ্রান্তিকর শব্দ হতে পারে, কারণ ত্রিভুজ নিজেই একই মেমরির একই স্থানে থাকে।

কোনও বস্তুর সরাতে কিছু সংস্থানের মালিকানা স্থানান্তরিত করার অর্থ এটি অন্য বস্তুতে পরিচালনা করে।

auto_ptr অনুলিপি auto_ptr সম্ভবত auto_ptr মতো কিছু দেখায় (কিছুটা সরলীকৃত):

auto_ptr(auto_ptr& source)   // note the missing const
{
    p = source.p;
    source.p = 0;   // now the source no longer owns the object
}

বিপজ্জনক এবং harmless প্যাচসমূহ

auto_ptr সম্পর্কে বিপজ্জনক জিনিসটি যা একটি কপি মত সিনট্যাক্সিকভাবে দেখায় আসলে একটি পদক্ষেপ। স্বয়ংক্রিয়_প্রিটর থেকে একটি সদস্য ফাংশন কল করার চেষ্টা করা অনির্ধারিত আচরণকে আহ্বান করবে, তাই আপনাকে সতর্কতা অবলম্বন করতে হবে যে এটি auto_ptr পরে এটি auto_ptr ব্যবহার করা যাবে না:

auto_ptr<Shape> a(new Triangle);   // create triangle
auto_ptr<Shape> b(a);              // move a into b
double area = a->area();           // undefined behavior

কিন্তু auto_ptr সবসময় বিপজ্জনক নয়। ফ্যাক্টরি ফাংশন auto_ptr জন্য পুরোপুরি সূক্ষ্ম ব্যবহার কেস:

auto_ptr<Shape> make_triangle()
{
    return auto_ptr<Shape>(new Triangle);
}

auto_ptr<Shape> c(make_triangle());      // move temporary into c
double area = make_triangle()->area();   // perfectly safe

নোট উভয় উদাহরণ একই সিনট্যাক্সিক প্যাটার্ন অনুসরণ করুন:

auto_ptr<Shape> variable(expression);
double area = expression->area();

এবং এখনো, তাদের মধ্যে একজন অনির্দিষ্ট আচরণকে আহ্বান করে, অন্যটি না করে। সুতরাং a এবং make_triangle() এক্সপ্রেশনগুলির মধ্যে পার্থক্য কী? তারা একই ধরনের উভয় না? প্রকৃতপক্ষে তারা, কিন্তু তারা বিভিন্ন মান বিভাগ আছে

মূল্য বিভাগ

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

যেমন তরল থেকে সরানো বিপজ্জনক, কারণ আমরা পরে একটি সদস্য ফাংশন কল করার চেষ্টা করতে পারে, অনির্ধারিত আচরণ invoking। অন্যদিকে, make_triangle() মত make_triangle() থেকে সরানোটি পুরোপুরি নিরাপদ, কারণ অনুলিপি কন্সট্রাকটর তার কাজ শেষ করার পরে, আমরা আবার অস্থায়ী ব্যবহার করতে পারি না। অস্থায়ী বলেন যে কোন অভিব্যক্তি আছে; যদি আমরা সহজভাবে make_triangle() আবার লিখি, আমরা একটি ভিন্ন অস্থায়ী পেতে পারি। আসলে, সরানো অস্থায়ী ইতিমধ্যে পরবর্তী লাইন চলে গেছে:

auto_ptr<Shape> c(make_triangle());
                                  ^ the moved-from temporary dies right here

মনে রাখবেন যে l এবং r অক্ষরের বাম দিকের দিকে এবং ডানদিকে একটি ঐতিহাসিক উত্স রয়েছে। এটি সি ++ তে আর সত্য নয়, কারণ এমন কিছু তরল রয়েছে যা কোন অ্যাসাইনমেন্টের বাম দিকের দিকে উপস্থিত হতে পারে না (যেমন অ্যাসাইন অপারেটর ছাড়া অ্যারে বা ব্যবহারকারী-সংজ্ঞায়িত ধরনের), এবং রেভালুগুলি যা করতে পারে (শ্রেণী প্রকারের সমস্ত প্রকারভেদগুলি) একটি নিয়োগ অপারেটর সঙ্গে)।

শ্রেণির ধরণের একটি র্যাভেলু একটি অভিব্যক্তি যার মূল্যায়ন একটি অস্থায়ী বস্তু তৈরি করে। স্বাভাবিক পরিস্থিতিতে, একই সুযোগের মধ্যে অন্য কোনও অভিব্যক্তি একই অস্থায়ী বস্তুর অর্থ দেয় না।

Rvalue রেফারেন্স

আমরা এখন lvalues ​​থেকে সরানো সম্ভাব্য বিপজ্জনক বুঝতে, কিন্তু Rvalues ​​থেকে সরানো harmless হয়। R ++ আর্গুমেন্টগুলি থেকে লভ্যু আর্গুমেন্টগুলিকে পৃথক করার জন্য C ++ ভাষা সমর্থন থাকলে আমরা লভ্য থেকে সম্পূর্ণভাবে নিষিদ্ধ করতে পারতাম, অথবা কমপক্ষে কল সাইটে স্পষ্টভাবে লভ্যগুলি থেকে সরাতে পারি, যাতে আমরা আর দুর্ঘটনাক্রমে অগ্রসর হব না।

এই সমস্যাটির C ++ 11 এর উত্তরটি র্যালু রেফারেন্স । একটি rvalue রেফারেন্স একটি নতুন ধরনের রেফারেন্স যা শুধুমাত্র রেভালুকে সংযুক্ত করে এবং সিনট্যাক্স X&& । ভাল পুরানো রেফারেন্স X& এখন একটি lvalue রেফারেন্স হিসাবে পরিচিত হয়। (উল্লেখ্য যে X&& একটি রেফারেন্সের রেফারেন্স নয়; সি ++ এ এমন কোনও জিনিস নেই।)

যদি আমরা মিশ্রণের মধ্যে নিক্ষেপ নিক্ষেপ, আমরা ইতিমধ্যে চার বিভিন্ন ধরনের রেফারেন্স আছে। টাইপ X এক্সপ্রেশন কি ধরনের তারা বাঁধতে পারেন?

            lvalue   const lvalue   rvalue   const rvalue
---------------------------------------------------------              
X&          yes
const X&    yes      yes            yes      yes
X&&                                 yes
const X&&                           yes      yes

অনুশীলনে, আপনি const X&& ; সম্পর্কে ভুলে যেতে পারেন। Rvalues ​​থেকে পড়া সীমিত হচ্ছে খুব দরকারী নয়।

একটি rvalue রেফারেন্স X&& একটি নতুন ধরনের রেফারেন্স যা শুধুমাত্র রেভুলিউসের সাথে যুক্ত হয়।

অনুলিপি রূপান্তর

Rvalue রেফারেন্স বিভিন্ন সংস্করণ মাধ্যমে গিয়েছিলাম। সংস্করণ 2.1 থেকে, একটি রেভেলু রেফারেন্স X&& এন্ড এ একটি ভিন্ন ধরনের Y এর সমস্ত মান বিভাগের সাথে সংযুক্ত থাকে, যদি Y থেকে X পর্যন্ত একটি অন্তর্নিহিত রূপান্তর হয়। এই ক্ষেত্রে, টাইপ X এর একটি অস্থায়ী তৈরি করা হয়, এবং র্যাভেল রেফারেন্সটি অস্থায়ীভাবে আবদ্ধ হয়:

void some_function(std::string&& r);

some_function("hello world");

উপরোক্ত উদাহরণে, "hello world" টাইপ কনস্টেবল const char[12]const char[12] const char* থেকে std::string মাধ্যমে const char* const char[12] একটি নিরবচ্ছিন্ন রূপান্তর রয়েছে, std::string একটি অস্থায়ী রূপ তৈরি করা হয়েছে, এবং r সেই অস্থায়ী r আবদ্ধ। এটি এমন এক ক্ষেত্রে যেখানে রেভালিউস (এক্সপ্রেশন) এবং সাময়িকগুলি (বস্তু) -এর মধ্যে পার্থক্যটি কিছুটা অস্পষ্ট।

কন্সট্রাক্টর সরান

একটি X&& প্যারামিটারের সাথে একটি ফাংশনের একটি কার্যকর উদাহরণ হল পদক্ষেপ X&& X::X(X&& source) । এর উদ্দেশ্য পরিচালিত সংস্থার মালিকানাটিকে উৎস থেকে বর্তমান বস্তুর মধ্যে হস্তান্তর করা।

সি ++ 11 এ, std::auto_ptr<T> দ্বারা প্রতিস্থাপিত হয়েছে যা র্যালিউ রেফারেন্সগুলির সুবিধা নেয়। আমি unique_ptr একটি সরলীকৃত সংস্করণ বিকাশ এবং আলোচনা হবে। প্রথম, আমরা একটি কাঁচা পয়েন্টার encapsulate এবং অপারেটর ওভারলোড -> এবং * , তাই আমাদের বর্গ একটি পয়েন্টার মত মনে হয়:

template<typename T>
class unique_ptr
{
    T* ptr;

public:

    T* operator->() const
    {
        return ptr;
    }

    T& operator*() const
    {
        return *ptr;
    }

কনস্ট্রাক্টর বস্তুর মালিকানা নেয় এবং ধ্বংসকারী এটি মুছে ফেলে:

    explicit unique_ptr(T* p = nullptr)
    {
        ptr = p;
    }

    ~unique_ptr()
    {
        delete ptr;
    }

এখন আকর্ষণীয় অংশ আসে, পদক্ষেপ গঠনকারী:

    unique_ptr(unique_ptr&& source)   // note the rvalue reference
    {
        ptr = source.ptr;
        source.ptr = nullptr;
    }

এই পদক্ষেপ কন্সট্রাকটর ঠিক auto_ptr কপি কন্সট্রকটার কি করেছে, কিন্তু এটি শুধুমাত্র রেভেলুজের সাথে সরবরাহ করা যেতে পারে:

unique_ptr<Shape> a(new Triangle);
unique_ptr<Shape> b(a);                 // error
unique_ptr<Shape> c(make_triangle());   // okay

দ্বিতীয় লাইনটি কম্পাইল করতে ব্যর্থ হয়, কারণ এটি একটি তরল, তবে প্যারামিটার unique_ptr&& source শুধুমাত্র রেভালিউসগুলির সাথে আবদ্ধ হতে পারে। এই ঠিক কি আমরা চেয়েছিলেন; বিপজ্জনক প্যাচসমূহ অন্তর্নিহিত করা উচিত নয়। তৃতীয় লাইনটি ঠিক সূক্ষ্ম করে make_triangle() , কারণ make_triangle() একটি র্যালু। পদক্ষেপ কন্সট্রাক্টর অস্থায়ী থেকে স্বতন্ত্র হস্তান্তর হস্তান্তর করবে। আবার, এই ঠিক কি আমরা চেয়েছিলেন।

সরানো কারিগর বর্তমান বস্তুর মধ্যে পরিচালিত সংস্থার মালিকানা স্থানান্তর করে।

নিয়োগ অপারেটর সরান

শেষ অনুপস্থিত টুকরা পদক্ষেপ বরাদ্দ অপারেটর। এর কাজ পুরাতন সম্পদ প্রকাশ এবং তার যুক্তি থেকে নতুন সম্পদ অর্জন করা হয়:

    unique_ptr& operator=(unique_ptr&& source)   // note the rvalue reference
    {
        if (this != &source)    // beware of self-assignment
        {
            delete ptr;         // release the old resource

            ptr = source.ptr;   // acquire the new resource
            source.ptr = nullptr;
        }
        return *this;
    }
};

নোট অ্যাসাইনমেন্ট অপারেটরটির এই বাস্তবায়নটি কীভাবে ধ্বংসকারী এবং পদক্ষেপ নির্মাতা উভয়ের যুক্তিটিকে সদৃশ করে। আপনি কপি এবং swap ছদ্মবেশ সঙ্গে পরিচিত? এটি স্যাম্যান্টিক্সকে সরানো-এবং-সোয়াপ idiom হিসাবে সরানোর জন্য প্রয়োগ করা যেতে পারে:

    unique_ptr& operator=(unique_ptr source)   // note the missing reference
    {
        std::swap(ptr, source.ptr);
        return *this;
    }
};

এখন যে source unique_ptr টাইপের একটি পরিবর্তনশীল, এটি পদক্ষেপ unique_ptr দ্বারা শুরু করা হবে; অর্থাৎ, যুক্তি পরামিতি মধ্যে সরানো হবে। যুক্তি এখনও একটি rvalue হতে প্রয়োজন, কারণ পদক্ষেপ গঠনকারী নিজেই একটি rvalue রেফারেন্স পরামিতি আছে। যখন কন্ট্রোল প্রবাহ operator= ক্লোজিং বোস পৌঁছে যায়, তখন source স্বয়ংক্রিয়ভাবে পুরানো সংস্থানটি ছেড়ে দেয়, সুযোগের বাইরে চলে যায়।

পদক্ষেপ অ্যাসাইনমেন্ট অপারেটর বর্তমান বস্তুর মধ্যে পরিচালিত সংস্থানের মালিকানা স্থানান্তর করে, পুরানো সংস্থানটি ছেড়ে দেয়। পদক্ষেপ-এবং-সোয়াপ idiom বাস্তবায়ন সহজ করে।

Lvalues ​​থেকে সরানো

কখনও কখনও, আমরা lvalues ​​থেকে সরানো করতে চান। অর্থাৎ, কখনও কখনও আমরা কম্পাইলারটিকে একটি তরঙ্গ হিসাবে চিকিত্সা করতে চাই, যেন এটি একটি র্যাভ্যুও ছিল, তাই এটি সরল অনিরাপদ হতে পারে, যদিও এটি সরানো পদক্ষেপ নির্মাতাটিকে আহ্বান করতে পারে। এই উদ্দেশ্যে, সি ++ 11 শিরোনাম <utility> ভিতরে std::move নামে পরিচিত একটি স্ট্যান্ডার্ড লাইব্রেরি ফাংশন টেমপ্লেট অফার করে। এই নামটি কিছুটা দুর্ভাগ্যজনক, কারণ std::move কেবল একটি র্যাভ্যুতে একটি লভ্যাকে ধারণ করে; এটা নিজেই কিছু সরানো না । এটা কেবল চলন্ত সক্রিয় করে। সম্ভবত এটি std::cast_to_rvalue বা std::enable_move নামকরণ করা উচিত ছিল, তবে আমরা এখন নাম দিয়ে আটকে std::enable_move

আপনি স্পষ্টভাবে একটি lvalue থেকে সরানো কিভাবে এখানে:

unique_ptr<Shape> a(new Triangle);
unique_ptr<Shape> b(a);              // still an error
unique_ptr<Shape> c(std::move(a));   // okay

উল্লেখ্য যে তৃতীয় লাইনের পরে, একটি ত্রিভুজ আর নেই। এটি ঠিক আছে, কারণ std::move(a) স্পষ্টভাবে লেখার কারণে, আমরা আমাদের উদ্দেশ্যগুলি পরিষ্কার করে দিয়েছি: "প্রিয় কন্সট্রকটর, c শুরু করার জন্য আপনি যা চান তা করুন; আমি আর যত্ন নিই না। a সঙ্গে আপনার উপায়। "

std::move(some_lvalue) একটি রেভালুতে একটি লভ্য বহন করে, এইভাবে পরবর্তী পদক্ষেপ সক্ষম করে।

Xvalues

দ্রষ্টব্যঃ যদিও std::move(a) একটি র্যালুও হয় তবে তার মূল্যায়ন একটি অস্থায়ী বস্তু তৈরি করে না । এই conundrum কমিটি একটি তৃতীয় মান বিষয়শ্রেণীতে পরিচয় করিয়ে বাধ্য। ঐতিহ্যগত অর্থে একটি র্যালিউও না হলেও এটি একটি র্যাভেল রেফারেন্সের সাথে আবদ্ধ হতে পারে, এটি একটি xvalue (এক্সপিরিং মান) বলা হয়। ঐতিহ্যগত রেভাগুলিকে নামকরণ করা হয়েছে (বিশুদ্ধ রেভেলিউস)।

উভয় prvalues ​​এবং xvalues ​​rvalues ​​হয়। Xvalues ​​এবং lvalues ​​উভয় glvalues (সাধারণ লভ্য) হয়। সম্পর্কগুলি একটি চিত্রের সাথে উপলব্ধ করা সহজ:

        expressions
          /     \
         /       \
        /         \
    glvalues   rvalues
      /  \       /  \
     /    \     /    \
    /      \   /      \
lvalues   xvalues   prvalues

উল্লেখ্য যে শুধুমাত্র xvalues ​​সত্যিই নতুন; বাকি শুধু পুনর্গঠন এবং গ্রুপিং কারণে।

C ++ 98 Rvalues ​​C ++ 11 এ প্রvalিউস হিসাবে পরিচিত। পূর্ববর্তী অনুচ্ছেদে "প্রভ্যুই" সহ "র্যাভলু" এর সমস্ত ঘটনার মানসিকভাবে প্রতিস্থাপন করুন।

ফাংশন আউট মুভিং

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

unique_ptr<Shape> make_triangle()
{
    return unique_ptr<Shape>(new Triangle);
}          \-----------------------------/
                  |
                  | temporary is moved into c
                  |
                  v
unique_ptr<Shape> c(make_triangle());

সম্ভবত বিস্ময়করভাবে, স্বয়ংক্রিয় বস্তুগুলি (স্থানীয় ভেরিয়েবল যা static হিসাবে ঘোষণা করা হয় না) এছাড়াও নিখুঁতভাবে ফাংশন থেকে সরানো যেতে পারে:

unique_ptr<Shape> make_square()
{
    unique_ptr<Shape> result(new Square);
    return result;   // note the missing std::move
}

কিভাবে পদক্ষেপ কন্সট্রাকটর একটি যুক্তি হিসাবে lvalue result গ্রহণ? result সুযোগ শেষ হয়, এবং স্ট্যাক unwinding সময় এটি ধ্বংস করা হবে। কেউ সম্ভবত পরে অভিযোগ করতে পারে যে result কোনভাবে পরিবর্তিত হয়েছে; যখন কন্ট্রোল প্রবাহ কলারে ফিরে আসে, result আর বিদ্যমান নেই! সেই কারণে, সি ++ 11 এর একটি বিশেষ নিয়ম রয়েছে যা std::move লিখতে না std::move স্বয়ংক্রিয় অবজেক্টগুলি ফাংশন থেকে ফেরত দেয়। প্রকৃতপক্ষে, স্বয়ংক্রিয় ক্রিয়াকলাপগুলিকে ফাংশন থেকে সরাতে std::move ব্যবহার করা উচিত নয় , কারণ এটি "নামযুক্ত ফেরত মান অপ্টিমাইজেশান" (NRVO) বাধা দেয়।

স্বয়ংক্রিয় বস্তু ফাংশন থেকে সরাতে std::move ব্যবহার করবেন না।

উল্লেখ্য যে উভয় ফ্যাক্টরী ফাংশনগুলিতে, রিটার্ন টাইপ একটি মান, একটি র্যালু রেফারেন্স নয়। Rvalue রেফারেন্স এখনও রেফারেন্স, এবং সবসময় হিসাবে, আপনি একটি স্বয়ংক্রিয় বস্তুর একটি রেফারেন্স ফিরে না করা উচিত; আপনি যদি আপনার কোডটি গ্রহণ করতে কম্পাইলারকে প্রতারিত করে ফেলেন তবে কলকারী একটি ঝলকপূর্ণ রেফারেন্সের সাথে শেষ হয়ে যাবে:

unique_ptr<Shape>&& flawed_attempt()   // DO NOT DO THIS!
{
    unique_ptr<Shape> very_bad_idea(new Square);
    return std::move(very_bad_idea);   // WRONG!
}

Rvalue রেফারেন্স দ্বারা স্বয়ংক্রিয় বস্তু ফিরে না। মুভিং কেবল সরানো পদক্ষেপ দ্বারা সঞ্চালিত হয়, std::move দ্বারা নয়, এবং কেবল একটি রেভালু রেফারেন্সে একটি রেভালুকে বাঁধাই করে না।

সদস্যদের মধ্যে সরানো

খুব শীঘ্রই বা পরে, আপনি এই ধরনের কোড লিখতে যাচ্ছেন:

class Foo
{
    unique_ptr<Shape> member;

public:

    Foo(unique_ptr<Shape>&& parameter)
    : member(parameter)   // error
    {}
};

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

একটি নাম্বার রেভালিউ রেফারেন্সটি অন্য কোনও পরিবর্তনশীলের মতো একটি তরল।

সমাধানটি নিজে চালানো সক্ষম করে:

class Foo
{
    unique_ptr<Shape> member;

public:

    Foo(unique_ptr<Shape>&& parameter)
    : member(std::move(parameter))   // note the std::move
    {}
};

member সূচনা করার পরে আপনি যে parameter আর ব্যবহার করেন না তা যুক্তিযুক্ত করতে পারেন। std::move জন্য কোনও বিশেষ নিয়ম নেই কেন? সম্ভবত এটি কম্পাইলার বাস্তবায়নের উপর অত্যধিক বোঝা হবে কারণ। উদাহরণস্বরূপ, যদি কনস্ট্রাক্টর শরীর অন্য অনুবাদ ইউনিটে ছিল? এর বিপরীতে, ফিরতি মূল্যের নিয়মটি প্রত্যক্ষকারী টেবিলগুলি প্রত্যক্ষ করতে হবে যে শব্দের পরে শনাক্তকারী শব্দটি স্বয়ংক্রিয় অণুটিকে নির্দেশ করে কিনা তা নির্ধারণ করতে।

আপনি মান দ্বারা parameter পাস করতে পারেন। unique_ptr মত সরানো-প্রকারের জন্য, এটি এখনও কোন প্রতিষ্ঠিত idiom আছে বলে মনে হয়। ব্যক্তিগতভাবে, আমি মান দ্বারা পাস পছন্দ করি, কারণ এটি ইন্টারফেসে কম ক্লাস্টারকে কারণ করে।

বিশেষ সদস্য ফাংশন

সি ++ 98 দাবিতে তিনটি বিশেষ সদস্য ফাংশন ঘোষণা করে, যা যখন প্রয়োজন হয় তখন: কপি কন্সট্রাকটর, কপি অ্যাসাইনমেন্ট অপারেটর এবং ধ্বংসকারী।

X::X(const X&);              // copy constructor
X& X::operator=(const X&);   // copy assignment operator
X::~X();                     // destructor

Rvalue রেফারেন্স বিভিন্ন সংস্করণ মাধ্যমে গিয়েছিলাম। সংস্করণ 3.0 থেকে, সি ++ 11 দাবিতে দুটি অতিরিক্ত বিশেষ সদস্য ফাংশন ঘোষণা করে: পদক্ষেপ গঠনকারী এবং পদক্ষেপ বরাদ্দ অপারেটর। দ্রষ্টব্য যে VC10 বা VC11 এখনও সংস্করণ 3.0 এর সাথে সামঞ্জস্যপূর্ণ নয়, তাই আপনাকে তাদের নিজের প্রয়োগ করতে হবে।

X::X(X&&);                   // move constructor
X& X::operator=(X&&);        // move assignment operator

এই দুটি নতুন বিশেষ সদস্য ফাংশন কেবলমাত্র ঘোষণা করা হয় যদি বিশেষ সদস্য ফাংশন ম্যানুয়ালি ঘোষণা করা হয় না। এছাড়াও, আপনি যদি নিজের পদক্ষেপ সরানো নির্মাতা বা অ্যাসাইনমেন্ট অপারেটরটি ঘোষণা করেন, তবে কপি কন্সট্রাকটর এবং কপি অ্যাসাইনমেন্ট অপারেটরও ঘোষণা করা হবে না।

এই নিয়ম অনুশীলন মানে কি?

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

নোট অ্যাসাইনমেন্ট অপারেটর এবং সরানো অ্যাসাইনমেন্ট অপারেটরটি মান অনুসারে তার যুক্তি গ্রহণ করে একটি একক, ইউনিফায়েড অ্যাসাইনমেন্ট অপারেটরে যুক্ত করা যেতে পারে।

X& X::operator=(X source)    // unified assignment operator
{
    swap(source);            // see my first answer for an explanation
    return *this;
}

এইভাবে, বিশেষ সদস্য ফাংশন সংখ্যা পাঁচ থেকে চার থেকে ড্রপ বাস্তবায়ন। এখানে ব্যতিক্রম-নিরাপত্তা এবং কার্যকারিতা মধ্যে একটি tradeoff আছে, কিন্তু আমি এই বিষয়ে বিশেষজ্ঞ নই।

ফরোয়ার্ড রেফারেন্স ( previously ইউনিভার্সাল রেফারেন্স হিসাবে পরিচিত)

নিম্নলিখিত ফাংশন টেমপ্লেট বিবেচনা করুন:

template<typename T>
void foo(T&&);

আপনি T&& & এটিকে শুধুমাত্র রেভালুতে বাঁধতে পারবেন বলে আশা করতে পারেন, কারণ প্রথম নজরে এটি একটি র্যালিউ রেফারেন্সের মতো মনে হচ্ছে। যদিও এটি দেখা যাচ্ছে, T&& এছাড়াও lvalues ​​সাথে binds:

foo(make_triangle());   // T is unique_ptr<Shape>, T&& is unique_ptr<Shape>&&
unique_ptr<Shape> a(new Triangle);
foo(a);                 // T is unique_ptr<Shape>&, T&& is unique_ptr<Shape>&

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

টি && একটি রেভেলিউ রেফারেন্স নয়, কিন্তু একটি ফরওয়ার্ডিং রেফারেন্স। এটি lvalues ​​এছাড়াও binds, যা ক্ষেত্রে T এবং T&& উভয় lvalue রেফারেন্স হয়।

আপনি যদি রেভালিউসগুলির জন্য একটি ফাংশন টেমপ্লেট সীমাবদ্ধ করতে চান, তবে আপনি টাইপ বৈশিষ্ট্যগুলির সাথে SFINAE একত্রিত করতে পারেন:

#include <type_traits>

template<typename T>
typename std::enable_if<std::is_rvalue_reference<T&&>::value, void>::type
foo(T&&);

পদক্ষেপ বাস্তবায়ন

এখন যেহেতু আপনি রেফারেন্স ভাঙচুর বোঝেন, এখানে std::move কিভাবে প্রয়োগ করা হয়:

template<typename T>
typename std::remove_reference<T>::type&&
move(T&& t)
{
    return static_cast<typename std::remove_reference<T>::type&&>(t);
}

আপনি যেহেতু দেখতে পারেন, move হওয়া রেফারেন্স T&& জন্য যেকোনো ধরনের প্যারামিটার ধন্যবাদ গ্রহণ করুন, এবং এটি একটি রেভ্যুই রেফারেন্স প্রদান করে। std::remove_reference<T>::type মেটা-ফাংশন কল প্রয়োজনীয় কারণ অন্যথায়, টাইপ X লভ্যতার জন্য, রিটার্ন টাইপ X& && হবে যা X& ধ্বসে পড়বে। যেহেতু টিটি সর্বদা একটি তরল (মনে রাখবেন যে নামযুক্ত রেভেলু রেফারেন্সটি একটি তরঙ্গ) তবে আমরা একটি রেভেলিউ রেফারেন্সে t আবদ্ধ করতে চাই, আমাদের স্পষ্টভাবে সঠিক রিটার্ন টাইপটি টিতে ঢুকতে হবে। একটি ফাংশন রেফারেন্স ফেরৎ যে একটি ফাংশন কল নিজেই একটি xvalue হয়। এখন আপনি জানেন কোলাকুলি কোথা থেকে এসেছে;)

একটি ফাংশন কল যা একটি rvalue রেফারেন্স প্রদান করে, যেমন std::move , একটি xvalue।

উল্লেখ্য যে Rvalue রেফারেন্স দ্বারা ফিরতি এই উদাহরণে সূক্ষ্ম, কারণ t একটি স্বয়ংক্রিয় বস্তুকে নির্দেশ করে না, বরং পরিবর্তে এমন একটি বস্তু যা কলার দ্বারা গৃহীত হয়েছিল।

আমি C++0x সম্পর্কিত স্কট মেয়ের্সের সাথে সফটওয়্যার ইঞ্জিনিয়ারিং রেডিও পডকাস্ট সাক্ষাত্কারটি C++0x । বেশিরভাগ নতুন বৈশিষ্ট্য আমার কাছে ইন্দ্রিয়গ্রাহ্য, এবং আমি আসলে ব্যতিক্রম ছাড়া C ++ 0x সম্পর্কে উত্তেজিত। আমি এখনও semantics সরানো না ... তারা ঠিক কি?


আমি উদাহরণ কোড সঙ্গে সরানো semantics বুঝতে সহজতর এটি। আসুন একটি খুব সাধারণ স্ট্রিং ক্লাস দিয়ে শুরু করি যা মেমরির হিপ-বরাদ্দকৃত ব্লকটিতে শুধুমাত্র পয়েন্টার রাখে:

#include <cstring>
#include <algorithm>

class string
{
    char* data;

public:

    string(const char* p)
    {
        size_t size = strlen(p) + 1;
        data = new char[size];
        memcpy(data, p, size);
    }

যেহেতু আমরা নিজেদের মেমরি পরিচালনা করতে বেছে নিয়েছি, তাই আমাদের তিনটি নিয়ম অনুসরণ করতে হবে। আমি অ্যাসাইনমেন্ট অপারেটরটি লিখতে বিলম্ব করব এবং এখন শুধুমাত্র ধ্বংসকারী এবং কপি কন্সট্রকটারটি বাস্তবায়ন করব:

    ~string()
    {
        delete[] data;
    }

    string(const string& that)
    {
        size_t size = strlen(that.data) + 1;
        data = new char[size];
        memcpy(data, that.data, size);
    }

কপি কন্সট্রাকটর স্ট্রিং বস্তুর অনুলিপি করার অর্থ কী তা সংজ্ঞায়িত করে। প্যারামিটার const string& that টাইপ স্ট্রিংয়ের সমস্ত এক্সপ্রেশনগুলিতে আবদ্ধ যা আপনাকে নিম্নলিখিত উদাহরণগুলিতে কপি করার অনুমতি দেয়:

string a(x);                                    // Line 1
string b(x + y);                                // Line 2
string c(some_function_returning_a_string());   // Line 3

এখন সরানো semantics মধ্যে মূল অন্তর্দৃষ্টি আসে। উল্লেখ্য যে শুধুমাত্র প্রথম লাইন যেখানে আমরা x অনুলিপি করি, এই গভীর অনুলিপিটি আসলেই প্রয়োজনীয়, কারণ আমরা পরে x পরিদর্শন করতে চাই এবং x যেকোনভাবে পরিবর্তিত হলে খুব অবাক হব। আপনি কি লক্ষ্য করেছেন যে আমি কিভাবে তিনটি বার x বলি (আপনি যদি এই বাক্যটি অন্তর্ভুক্ত করেন তবে চার বার) এবং প্রতিবার সঠিক একই বস্তু বোঝানো হয়? আমরা যেমন x "lvalues" এক্সপ্রেশন কল।

লাইন 2 এবং 3 এর আর্গুমেন্টগুলি লভ্য নয়, কিন্তু রেভেলিউস, কারণ অন্তর্নিহিত স্ট্রিং বস্তুর কোন নাম নেই, তাই ক্লায়েন্টের পরে সময়ে আবার তাদের পরিদর্শন করার কোন উপায় নেই। র্যাভলগুলি অস্থায়ী বস্তুগুলিকে নির্দেশ করে যা পরবর্তী সেমিকোলনটিতে ধ্বংস হয় (আরও সুনির্দিষ্ট হতে: পূর্ণ-অভিব্যক্তিটির শেষে যা ব্যাখ্যামূলকভাবে রেভালু ধারণ করে)। এটি গুরুত্বপূর্ণ কারণ b এবং c এর সূচনাকালে, আমরা সোর্স স্ট্রিংয়ের সাথে যা চেয়েছিলেন তা করতে পারি, এবং ক্লায়েন্ট কোন পার্থক্য বলতে পারছেন না !

সি ++ 0x "র্যালু রেফারেন্স" নামক একটি নতুন প্রক্রিয়া উপস্থাপন করে যা অন্যান্য জিনিসের মধ্যে আমাদের ফাংশন ওভারলোডিংয়ের মাধ্যমে র্যালুয়ে আর্গুমেন্ট সনাক্ত করতে দেয়। আমাদের যা করতে হবে তা একটি র্যাভেল রেফারেন্স প্যারামিটারের সাথে একটি কন্সট্রকটর লিখতে হয়। যে কন্সট্রকটরের ভিতর আমরা সোর্সের সাথে যা করতে চাই তা আমরা করতে পারি, যতক্ষণ আমরা এটি কোনও বৈধ অবস্থায় রেখে যাব:

    string(string&& that)   // string&& is an rvalue reference to a string
    {
        data = that.data;
        that.data = nullptr;
    }

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

অভিনন্দন, আপনি এখন semantics সরানো বেসিক বুঝতে! এসাইনমেন্ট অপারেটর বাস্তবায়ন করে চলুন। যদি আপনি অনুলিপি এবং সোয়াপ idiom সম্পর্কে অপরিচিত না হন তবে এটি শিখুন এবং ফিরে আসুন, কারণ এটি একটি অসাধারণ C ++ ব্যতিক্রম যা নিরাপত্তা সুরক্ষা সম্পর্কিত।

    string& operator=(string that)
    {
        std::swap(data, that.data);
        return *this;
    }
};

হু, এটা কি? "কোথায় rvalue রেফারেন্স?" আপনি জিজ্ঞাসা করতে পারেন। "আমাদের এখানে দরকার নেই!" আমার উত্তর :)

উল্লেখ্য যে আমরা পরামিতিটি মান অনুসারে পাস করি, যাতে that অন্য স্ট্রিং বস্তুর মতোই শুরু করতে হবে। ঠিক কিভাবে that চালু করা যাচ্ছে? C++98 এর পুরানো দিনের মধ্যে, উত্তর "কপি কন্সট্রকটর দ্বারা" হয়েছে। সি ++ 0x এ, কম্পাইলারটি কপি কন্সট্রাকটর এবং সরানো কন্সট্রকটারের মধ্যে চয়ন করে যা অ্যাসাইনমেন্ট অপারেটরের যুক্তিটি একটি তরল বা একটি র্যাভ্যু।

তাই যদি আপনি a = b বলে থাকেন, কপি কন্সট্রকটারটি শুরু করবে (কারণ এক্সটিটি হল একটি তরল), এবং অ্যাসাইনমেন্ট অপারেটরটি সাম্প্রতিক তৈরি, গভীর অনুলিপি সহ সামগ্রীগুলিকে স্যুইচ করে। এটি অনুলিপি এবং সোয়াপ idiom এর খুব সংজ্ঞা - একটি অনুলিপি তৈরি করুন, অনুলিপি দ্বারা সামগ্রীটি স্যুইপ করুন এবং তারপরে সুযোগ ছাড়িয়ে কপিটি পরিত্রাণ পান। এখানে নতুন কিছু নেই।

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

সংক্ষেপে, কপি কনস্ট্রাক্টর একটি গভীর অনুলিপি তৈরি করে, কারণ উৎসটি অপরিবর্তিত থাকা আবশ্যক। অন্যদিকে, সরানো কন্সট্রাকটরটি কেবল পয়েন্টারটি অনুলিপি করতে পারে এবং তারপরে উৎসের মধ্যে পয়েন্টারটিকে নিলতে সেট করতে পারে। সোর্স অবজেক্টকে এই পদ্ধতিতে "নিশ্চিহ্ন করা" ঠিক আছে, কারন ক্লায়েন্টটির আবার বস্তুর পরিদর্শন করার কোন উপায় নেই।

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


স্যাম্যান্টিক্স Rvalue রেফারেন্স উপর ভিত্তি করে সরানো
একটি rvalue একটি অস্থায়ী বস্তু, যা প্রকাশের শেষে ধ্বংস করা যাচ্ছে। বর্তমান C ++ এ, রেভেলু শুধুমাত্র কনস্ট রেফারেন্সগুলিতে আবদ্ধ। C ++ 1x অ-কনস্টেবল রেভালিউ রেফারেন্সগুলিকে অনুমতি দেবে, বানান T&& , যা একটি র্যাভ্যু অবজেক্টের রেফারেন্স।
যেহেতু একটি রেভালু একটি অভিব্যক্তি শেষে মারা যাচ্ছে, আপনি তার তথ্য চুরি করতে পারেন। অন্য বস্তুর মধ্যে এটি অনুলিপি করার পরিবর্তে, আপনি এটির ডেটা সরাতে পারেন।

class X {
public: 
  X(X&& rhs) // ctor taking an rvalue reference, so-called move-ctor
    : data_()
  {
     // since 'x' is an rvalue object, we can steal its data
     this->swap(std::move(rhs));
     // this will leave rhs with the empty data
  }
  void swap(X&& rhs);
  // ... 
};

// ...

X f();

X x = f(); // f() returns result as rvalue, so this calls move-ctor

উপরের কোডে, পুরানো কম্পাইলারগুলির সাথে f() এর ফলাফল X এর কপি কন্সট্রাকটর ব্যবহার করে x অনুলিপি করা হয়। আপনার কম্পাইলার স্থানান্তর শব্দ সমর্থন করে এবং X একটি পদক্ষেপ-কন্সট্রাকটর আছে, তাহলে তার পরিবর্তে বলা হয়। যেহেতু তার rhs argue একটি র্যালুউ হয় , আমরা জানি যে এটি আর প্রয়োজন নেই এবং আমরা এর মূল্য চুরি করতে পারি।
সুতরাং মানটি অনামী অস্থায়ী থেকে f() থেকে x ফিরে যায় (যখন x এর তথ্য, একটি খালি X প্রারম্ভিক, অস্থায়ী মধ্যে সরানো হয়, যা অ্যাসাইনমেন্টের পরে ধ্বংস হয়ে যাবে)।


Move semantics is about transferring resources rather than copying them when nobody needs the source value anymore.

C ++ 03 তে, কোনও কোড আবার মান ব্যবহার করার আগে বস্তুগুলিকে অনুলিপি করা হয়, শুধুমাত্র ধ্বংস বা বরাদ্দ করা হয়। উদাহরণস্বরূপ, যখন আপনি কোনও ফাংশন থেকে মান দ্বারা ফিরবেন-যদি না RVO kicks-আপনি যে মানটি ফেরত দিচ্ছেন তার কলারের স্ট্যাক ফ্রেমে অনুলিপি করা হয় এবং তারপরে এটি সুযোগ ছাড়িয়ে যায় এবং ধ্বংস হয়ে যায়। এটি কেবল অনেক উদাহরণের মধ্যে একটি: উৎস বস্তুটি যখন অস্থায়ী হয় তখন পাস-বাই-মানটি দেখুন, অ্যালগরিদমগুলি যেমন sortআইটেমগুলিকে পুনর্বিন্যস্ত করে, vectorতার capacity()অতিক্রম করার সময় পুনরায় স্থানান্তর ইত্যাদি।

যেমন কপি / ধ্বংস জোড়া ব্যয়বহুল যখন, এটি সাধারণত কিছু হেভিওয়েট সম্পদ মালিক কারণ এটি সাধারণত। উদাহরণস্বরূপ, বস্তুর vector<string>অ্যারের ধারণকারী একটি গতিশীলভাবে-বরাদ্দকৃত মেমরি ব্লক থাকতে stringপারে, প্রতিটি তার নিজের গতিশীল মেমরি সহ। যেমন একটি বস্তু অনুলিপি ব্যয়বহুল: আপনি উত্স প্রতিটি গতিশীলভাবে-বরাদ্দ ব্লক জন্য নতুন মেমরি বরাদ্দ করতে হবে, এবং সমস্ত মান কপি। তারপরে আপনি কেবলমাত্র অনুলিপি করা সমস্ত মেমরিটি হ্রাস করতে হবে। যাইহোক, একটি বড় উপায় সরানোvector<string> শুধু কয়েক পয়েন্টার (যা ডায়নামিক মেমরি ব্লক পড়ুন) গন্তব্য এবং ক্রিড়া তাদের উত্স আউট।


আমি এটা সঠিকভাবে বুঝতে আমি এই লিখছি।

বড় বস্তুর অপ্রয়োজনীয় অনুলিপি এড়ানোর জন্য সেমেটিকগুলি তৈরি করা হয়েছে। Bjarne Stroustrup তার বই "সি ++ প্রোগ্রামিং ভাষা" দুটি উদাহরণ ব্যবহার করে যেখানে অপ্রয়োজনীয় অনুলিপিটি ডিফল্টভাবে ঘটে: এক, দুটি বৃহত বস্তুর সোয়াপিং, এবং দুটি, একটি পদ্ধতি থেকে একটি বড় বস্তুর প্রত্যাবর্তন।

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

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

যেসব ভাষায় স্থানীয় বস্তুর সৃষ্টি (অর্থাৎ, স্ট্যাকের বস্তুগুলি) সৃষ্টি করার অনুমতি দেয় না, এই ধরনের সমস্যাগুলি ঘটে না কারণ সমস্ত বস্তু হিপে বরাদ্দ করা হয় এবং সর্বদা রেফারেন্স দ্বারা অ্যাক্সেস করা হয়।


এটি অনুলিপি স্যাম্যান্টিকস এর মতো, তবে আপনি যে সমস্ত তথ্য থেকে "সরানো" হচ্ছে তা থেকে তথ্য চুরি করার জন্য যে সমস্ত ডেটা আপনি পান তা অনুলিপি করার পরিবর্তে।


স্যাম্যান্টিক্স সরানো প্রয়োজন ব্যাখ্যা করার জন্য , চলুন semantics সরানো ছাড়া এই উদাহরণ বিবেচনা করা যাক:

এখানে একটি ফাংশন যা টাইপের একটি বস্তু নেয় Tএবং একই ধরণের একটি বস্তু প্রদান করে T:

T f(T o) { return o; }
  //^^^ new object constructed

উপরের ফাংশন ব্যবহার মান কল যার মানে যখন এই ফাংশন বলা হয় একটি বস্তু হতে হবে নির্মাণ ফাংশন দ্বারা ব্যবহৃত হবে।
কারন ফাংশন মান দ্বারা ফেরত দেয়, কারণ অন্য একটি নতুন অবজেক্ট রিটার্ন মানের জন্য তৈরি করা হয়:

T b = f(a);
  //^ new object constructed

দুটি নতুন বস্তু তৈরি করা হয়েছে, যার মধ্যে একটি একটি অস্থায়ী বস্তু যা শুধুমাত্র কার্যকালের জন্য ব্যবহৃত হয়।

যখন নতুন বস্তু ফেরত মান থেকে তৈরি হয়, তখন কপি কন্সট্রাকটরকে অস্থায়ী বস্তুর সামগ্রীটি নতুন বস্তুর প্রতি অনুলিপি করতে বলা হয় b। ফাংশনটি শেষ হওয়ার পরে, ফাংশনে ব্যবহৃত অস্থায়ী বস্তুটি সুযোগ ছাড়িয়ে যায় এবং ধ্বংস হয়।

এখন, একটি কপি কন্সট্রাকটর কি বিবেচনা করা যাক ।

এটি প্রথমে বস্তুটি আরম্ভ করতে হবে, তারপরে পুরানো বস্তুর সমস্ত প্রাসঙ্গিক তথ্য নতুনতে অনুলিপি করুন।
ক্লাসের উপর নির্ভর করে, এটি হয়তো অনেক বেশি তথ্য সহ একটি ধারক, তারপরে এটি অনেক সময় এবং মেমরির ব্যবহারকে উপস্থাপন করতে পারে

// Copy constructor
T::T(T &old) {
    copy_data(m_a, old.m_a);
    copy_data(m_b, old.m_b);
    copy_data(m_c, old.m_c);
}

সঙ্গে পদক্ষেপ শব্দার্থবিদ্যা এটা এখন কম অপ্রীতিকর এই কাজ থেকে সবচেয়ে ভাল অভিজ্ঞতা কেবল দ্বারা সম্ভব চলন্ত তথ্য বদলে অনুলিপি।

// Move constructor
T::T(T &&old) noexcept {
    m_a = std::move(old.m_a);
    m_b = std::move(old.m_b);
    m_c = std::move(old.m_c);
}

তথ্য মুভিং নতুন বস্তুর সাথে তথ্য পুনরায় যুক্ত জড়িত। এবং কোন কপি সব সময়ে সঞ্চালিত হয়।

এই একটি rvalueরেফারেন্স সঙ্গে সম্পন্ন করা হয় ।
একটি rvalueউল্লেখ lvalueএকটি গুরুত্বপূর্ণ পার্থক্য সঙ্গে একটি রেফারেন্স মত বেশ অনেক কাজ করে :
একটি rvalue রেফারেন্স সরানো যেতে পারে এবং একটি lvalue করতে পারেন না।

cppreference.com থেকে :

শক্তিশালী ব্যতিক্রম গ্যারান্টি সম্ভাব্য করতে, ব্যবহারকারী-সংজ্ঞায়িত পদক্ষেপ নির্মাতারা ব্যতিক্রম নিক্ষেপ করা উচিত নয়। প্রকৃতপক্ষে, স্ট্যান্ডার্ড কন্টেইনারগুলি সাধারণত স্ট ডি :: move_if_noexcept তে নির্ভর করে যখন কনটেইনার উপাদানগুলি স্থানান্তরিত করতে হবে তখন সরানো এবং অনুলিপি নির্বাচন করুন। উভয় কপি এবং সরানো হয় কন্সট্রাকটর সরবরাহ করা হলে, ওভারলোড রেজোলিউশন হ'ল যুক্তি কন্সট্রাকটর নির্বাচন করে যদি যুক্তিটি একটি র্যাভেলিউ (এটি একটি অনামী অস্থায়ী বা স্ট্যাড :: চলন্ত ফলাফলের মতো একটি xvalue), এবং কপি কন্সট্রকটার নির্বাচন করে যুক্তি একটি lvalue (নামযুক্ত বস্তু বা একটি ফাংশন / অপারেটর lvalue রেফারেন্স ফেরত)। যদি শুধুমাত্র কপি কন্সট্রকটার সরবরাহ করা হয়, তবে সমস্ত যুক্তি বিভাগগুলি এটি নির্বাচন করে (যতক্ষন পর্যন্ত এটি কনস্টেবলের একটি রেফারেন্স নেয়, যেহেতু রেভলিউসগুলি কনস্টেক্স রেফারেন্সগুলিতে আবদ্ধ হতে পারে), যা চলার জন্য ফাঁকা যাওয়ার জন্য ফাল্যাকব অনুলিপি করে। অনেক পরিস্থিতিতে,সরানো স্রষ্টাগুলি পর্যবেক্ষণযোগ্য পার্শ্বপ্রতিক্রিয়াগুলি উত্পন্ন করলে এমনকি কাস্টমাইজারগুলিকে অপ্টিমাইজ করা হয়, কপি Elision দেখুন। যখন এটি একটি প্যারামিটার হিসাবে একটি রেভ্যুই রেফারেন্স নেয় তখন একটি কন্সট্রাকটরকে 'পদক্ষেপ গঠনকারী' বলা হয়। কিছু সরানো বাধ্যতামূলক নয়, ক্লাসটিকে সরানোর জন্য সংস্থার প্রয়োজন নেই এবং একটি 'পদক্ষেপ নির্মাতা' অনুমতিপ্রাপ্ত (কিন্তু সম্ভবত বুদ্ধিমান) ক্ষেত্রে কোনও সংস্থান স্থানান্তর করতে সক্ষম হবেন না যেখানে প্যারামিটারটি রয়েছে Const rvalue রেফারেন্স (কনস্টেবল টি &&)।অনুমানযোগ্য (কিন্তু সম্ভবত বুদ্ধিমান) ক্ষেত্রে কোনও সংস্থান সরাতে সক্ষম নাও হতে পারে যেখানে প্যারামিটার একটি কনস্ট র্যালিউ রেফারেন্স (Const T &&)।অনুমানযোগ্য (কিন্তু সম্ভবত বুদ্ধিমান) ক্ষেত্রে কোনও সংস্থান সরাতে সক্ষম নাও হতে পারে যেখানে প্যারামিটার একটি কনস্ট র্যালিউ রেফারেন্স (Const T &&)।


আপনি কি জানেন একটি কপি শব্দটি মানে সঠিক? এর অর্থ হচ্ছে আপনার কাছে কপি করার যোগ্যগুলি রয়েছে, আপনি যে ব্যবহারকারী-সংজ্ঞায়িত প্রকারগুলি সংজ্ঞায়িত করেছেন সেগুলির জন্য এটি একটি কপি কন্সট্রাকটর এবং অ্যাসাইনমেন্ট অপারেটর স্পষ্টভাবে লেখার জন্য বা কম্পাইলারটি তাদের অন্তর্নিহিতভাবে জেনারেট করে। এই একটি কপি করবেন।

স্যাম্যান্টিক্সটি মূলত একটি ব্যবহারকারী-সংজ্ঞায়িত প্রকার যা কন্সট্রকটরের সাথে একটি র-মান রেফারেন্স গ্রহণ করে (নতুন প্রকারের রেফারেন্স ব্যবহার করে && (yes দুই ampersands)) যা অ-কনস্টেস্ট হয়, এটি একটি পদক্ষেপ কন্সট্রাকটর বলে, এটিও অ্যাসাইনমেন্ট অপারেটর হিসাবে যায়। সুতরাং একটি সরানো কন্সট্রাকটর কি করে, তার উৎসের যুক্তি থেকে মেমরি অনুলিপি করার পরিবর্তে এটি সোর্স থেকে গন্তব্য পর্যন্ত 'সরানো' হয়।

তুমি কখন এটা করতে চাও? ভাল std :: vector একটি উদাহরণ, আপনি একটি অস্থায়ী std :: vector তৈরি করেছেন এবং আপনি এটি একটি ফাংশন থেকে এটি ফেরত বলেছেন:

std::vector<foo> get_foos();

যখন ফাংশনটি ফেরত আসে তখন কপি কন্সট্রাকটর থেকে আপনি ওভারহেড করতে যাচ্ছেন, যদি (এবং এটি C ++ 0x এ হবে) std :: vector এর অনুলিপি করার পরিবর্তে একটি সরানো কন্সট্রাকটর রয়েছে এটি কেবল এটির পয়েন্টার সেট করে এবং গতিশীলভাবে বরাদ্দ করা যেতে পারে নতুন উদাহরণ মেমরি। এটি std :: auto_ptr এর সাথে ট্রান্সফার-অফ-মালিকানা সিমন্টিক্সের মতো।





move-semantics