চলন্ত শব্দার্থবিজ্ঞান কি?


1700

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


20
আমি [এলি বেন্ডারস্কির ব্লগ নিবন্ধ] ( eli.thegreenplace.net/2011/12/15/… ) সি এবং সি ++ এর লভ্য এবং মূল্যগুলি সম্পর্কে খুব তথ্যপূর্ণ found তিনি সি ++ 11-এ মূল মূল্যবোধের উল্লেখগুলি উল্লেখ করেছেন এবং ছোট ছোট উদাহরণগুলির সাথে তাদের পরিচয় করিয়ে দেন।
নীল


19
প্রতি বছর বা তাই আমি ভাবছি যে সি ++ এর "নতুন" মুভ শব্দার্থবিজ্ঞানগুলি কী, আমি এটি গুগল করে এই পৃষ্ঠায় পৌঁছেছি। আমি প্রতিক্রিয়াগুলি পড়ি, আমার মস্তিষ্ক বন্ধ হয়ে যায়। আমি সিতে ফিরে যাই, এবং সব ভুলে যাই! আমি অচল
আকাশ

7
@ এসকি স্ট্যান্ড করুন :: ভেক্টর <> বিবেচনা করুন ... কোথাও কোথাও গাদাতে একটি অ্যারের নির্দেশক রয়েছে। আপনি যদি এই বিষয়টিকে অনুলিপি করেন তবে একটি নতুন বাফার বরাদ্দ করতে হবে এবং বাফার থেকে ডেটা নতুন বাফারে অনুলিপি করা দরকার। এমন কোনও পরিস্থিতি আছে যেখানে কেবল পয়েন্টারটি চুরি করা ঠিক হবে? উত্তরটি হ্যাঁ, যখন সংকলক জানে বস্তুটি অস্থায়ী। মুভ সিমান্টিকস আপনাকে সংজ্ঞায়িত করতে দেয় যে কীভাবে আপনার ক্লাসের সাহসগুলি সরানো যায় এবং কোনও ভিন্ন বস্তুতে ফেলে দেওয়া যেতে পারে যখন সংকলক জানে যে আপনি যে বস্তুটি থেকে চলেছেন তা চলে যাচ্ছে।
ডিক্রোকস

আমি বুঝতে পারি কেবলমাত্র রেফারেন্স: learnncpp.com/cpp-tutorial/… , অর্থ্যাৎ মুভ সিমানটিক্সের মূল যুক্তিটি স্মার্ট পয়েন্টার থেকে।
jw_

উত্তর:


2478

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

#include <cstring>
#include <algorithm>

class string
{
    char* data;

public:

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

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

    ~string()
    {
        delete[] data;
    }

    string(const string& that)
    {
        size_t size = std::strlen(that.data) + 1;
        data = new char[size];
        std::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

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

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

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

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

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

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

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

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

নোট করুন যে আমরা প্যারামিটারটি that মান দ্বারা পাস করি , সুতরাং thatঅন্য স্ট্রিং অবজেক্টের মতোই আরম্ভ করতে হবে। ঠিক কীভাবে thatশুরু হতে চলেছে? সি ++ 98 এর পুরানো দিনগুলিতে , উত্তরটি "অনুলিপি নির্মাণকারীর দ্বারা" হত। সি ++ 0 এক্স-এ, অ্যাসাইনমেন্ট অপারেটরের আর্গুমেন্টটি একটি লভ্যালু বা কোনও মূল্যমান নয় কিনা তার উপর ভিত্তি করে সংকলকটি অনুলিপি নির্মাণকারী এবং মুভ কনস্ট্রাক্টরের মধ্যে নির্বাচন করে ses

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

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

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

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


40
@ তবে যদি আমার কর্টারের কোনও মূল্যমান হয়, যা পরে কখনও ব্যবহার করা যায় না, তবে কেন আমি এটিকে একটি ধারাবাহিক / নিরাপদ অবস্থায় রেখে বিরক্ত করার দরকার নেই কেন? সেই.ডেটা = 0 সেট করার পরিবর্তে, কেন এটি ছেড়ে দেওয়া হবে না?
einpoklum

70
@ আইনপোকলুম কারণ that.data = 0, চরিত্রগুলি খুব তাড়াতাড়ি ধ্বংস হয়ে যাবে (যখন অস্থায়ী মারা যায়), এবং দু'বারও । আপনি ডেটা চুরি করতে চান, শেয়ার করবেন না!
ফ্রেডওভারফ্লো

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

12
উত্তর এবং যেহেতু আমরা চাই না যে চর অ্যারেটি মুছে ফেলা হবে যখন এটি ঘটে, তাই আমাদের পয়েন্টারটি বাতিল করতে হবে।
ফ্রেডওভারফ্লো

7
delete[]একটি নাল্প্টারে @ ভাইরাস 721 সি ++ স্ট্যান্ডার্ড দ্বারা নো-ওপেন হিসাবে সংজ্ঞায়িত করা হয়েছে।
ফ্রেডওভারফ্লো 21

1057

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

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

ভূমিকা

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

  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, একটি অবচিত সি ++ 98 স্ট্যান্ডার্ড লাইব্রেরি টেম্পলেট, যা std::unique_ptrসি ++ 11 এ প্রতিস্থাপন করা হয়েছিল। ইন্টারমিডিয়েট সি ++ প্রোগ্রামার সম্ভবত কমপক্ষে কিছুটা হলেও পরিচিত std::auto_ptrএবং "মুভ শব্দার্থক" এর কারণে এটি প্রদর্শিত হয়, এটি সি ++ 11 এ মুভ শব্দার্থবিজ্ঞানের আলোচনার জন্য একটি ভাল সূচনার পয়েন্ট বলে মনে হচ্ছে। YMMV।

একটি চাল কি?

সি ++ 98 স্ট্যান্ডার্ড লাইব্রেরিটিতে একটি অনন্য মালিকানা শব্দার্থক কলযুক্ত একটি স্মার্ট পয়েন্টার সরবরাহ করে 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নেই না ত্রিভুজ কপি, কিন্তু এর পরিবর্তে থেকে ত্রিভুজ মালিকানা স্থানান্তর aকরতে b। আমরা বলুন " aহয় স্থানান্তরিত b বা" ত্রিভুজ হয় " সরানো থেকে a থেকে b "। এটি বিভ্রান্তিকর লাগতে পারে কারণ ত্রিভুজ নিজেই সর্বদা স্মৃতিতে একই জায়গায় থাকে।

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

এর অনুলিপি নির্মাতাকে 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
}

বিপজ্জনক এবং নিরীহ পদক্ষেপ

বিপজ্জনক বিষয়টি 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()? তারা উভয় কি একই ধরণের নয়? প্রকৃতপক্ষে তারা, কিন্তু তাদের বিভিন্ন মানের বিভাগ রয়েছে

মান বিভাগ

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

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

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

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

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

মূল্যসূত্র উল্লেখ

আমরা এখন বুঝতে পারি যে ল্যাভেলুগুলি থেকে সরানো সম্ভাব্য বিপজ্জনক, তবে মূল্যবোধগুলি থেকে সরানো নিরীহ। যদি সি ++ এর সাথে মূল্যবান আর্গুমেন্টগুলিকে মূল্যবান আর্গুমেন্টগুলির মধ্যে পার্থক্য জানাতে ভাষা সমর্থন ছিল, তবে আমরা হয় সমপরিমাণ থেকে সরানো সম্পূর্ণ নিষেধ করতে পারি, বা কমপক্ষে কল সাইটে স্পষ্টত ল্যাভেলুগুলি থেকে সরে যেতে নিষেধ করতে পারি, যাতে আমরা দুর্ঘটনার ফলে আর চলি না।

এই সমস্যার জন্য সি ++ 11 এর উত্তর হ'ল মূল্য উল্লেখ । একটি মূল্যমানের রেফারেন্স একটি নতুন ধরণের রেফারেন্স যা কেবল মূল্যগুলির সাথে আবদ্ধ হয় এবং বাক্য গঠনটি হয় X&&। ভাল পুরানো রেফারেন্স X&এখন একটি মূল্যবান রেফারেন্স হিসাবে পরিচিত । (নোট যে X&&হয় না একটি রেফারেন্স একটি রেফারেন্স, সেখানে সি যেমন জিনিস আছে ++,।)

যদি আমরা constমিশ্রণটি ফেলে দিই তবে ইতিমধ্যে আমাদের চারটি বিভিন্ন ধরণের রেফারেন্স রয়েছে। তারা কী ধরণের ধরণের প্রকাশের সাথে Xবাঁধতে পারে?

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

অনুশীলনে, আপনি সম্পর্কে ভুলে যেতে পারেন const X&&। মূল্যবোধগুলি থেকে পড়া সীমাবদ্ধ হওয়া খুব কার্যকর নয়।

মূল্যমানের উল্লেখটি X&&একটি নতুন ধরণের রেফারেন্স যা কেবল মূল্যগুলির সাথে আবদ্ধ val

অন্তর্নিহিত রূপান্তর

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

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

some_function("hello world");

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

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

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

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

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

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

মুভ কনস্ট্রাক্টর একটি পরিচালিত সংস্থার মালিকানা বর্তমান অবজেক্টে স্থানান্তর করে।

অ্যাসাইনমেন্ট অপারেটরদের সরান

সর্বশেষ অনুপস্থিত অংশটি হ'ল মুভ অ্যাসাইনমেন্ট অপারেটর। এটির কাজটি পুরানো সংস্থানটি প্রকাশ করা এবং তার যুক্তি থেকে নতুন সংস্থান অর্জন করা:

    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;
    }
};

নোট করুন এবং মুভ কনস্ট্রাক্টর উভয়ের লভিকে নকল করে কীভাবে মুভ অ্যাসাইনমেন্ট অপারেটরের এই বাস্তবায়নটি নোট করুন Note আপনি কি অনুলিপি-অনুলিপি সঙ্গে পরিচিত? এটি মুভ-ও-অদল-বদল আইডিয়াম হিসাবে শব্দার্থক স্থানান্তর করতে প্রয়োগ করা যেতে পারে:

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

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

মুভ অ্যাসাইনমেন্ট অপারেটর পুরানো রিসোর্সটি প্রকাশ করে একটি পরিচালিত সংস্থার মালিকানা বর্তমান অবজেক্টে স্থানান্তর করে। মুভ-অ-অদলবদ আইডিয়ম বাস্তবায়নকে সহজতর করে।

ল্যাভেলু থেকে চলন্ত

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

আপনি কীভাবে স্পষ্টতই একটি মূল্যবান স্থান থেকে সরান তা এখানে:

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

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

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

Xvalues

দ্রষ্টব্য যে যদিও std::move(a)এটি একটি মূল্যায়ন হলেও এটির মূল্যায়ন কোনও অস্থায়ী বস্তু তৈরি করে না । এই গোপনীয়তা কমিটিটিকে তৃতীয় মানের বিভাগ প্রবর্তন করতে বাধ্য করেছিল। সনাতন অর্থে এটি কোনও মূল্যবোধ না হলেও এমন কোনও কিছুকে যে কোনও মূল্যের রেফারেন্সের সাথে আবদ্ধ হতে পারে তাকে এক্সভ্যালু (এক্সপায়ারিং মান) বলা হয়। Traditionalতিহ্যবাহী মূল্যবোধগুলির নাম পরিবর্তন করে দেওয়া হয়েছিল (খাঁটি মূল্য)।

মূল্য এবং এক্সভ্যালু উভয়ই মূল্যবোধ। এক্সভালিউস এবং ল্যাভেলুগুলি উভয়ই গ্লাভ (সাধারণ জালযুক্ত)। চিত্রগুলি চিত্রের সাহায্যে উপলব্ধি করা আরও সহজ:

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

মনে রাখবেন যে শুধুমাত্র এক্সভ্যালুগুলিই নতুন; বাকিটি কেবল নামকরণ এবং গ্রুপিংয়ের কারণে।

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

ফাংশন থেকে সরানো

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

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
}

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

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

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

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

মূল্যবোধের রেফারেন্স দ্বারা স্বয়ংক্রিয় বস্তুগুলি কখনই ফিরিয়ে আনবেন না। মুভিং একচেটিয়াভাবে মুভ কনস্ট্রাক্টর দ্বারা সঞ্চালিত হয়, দ্বারা নয় std::move, এবং কেবল কোনও মূল্যের রেফারেন্সের সাথে কোনও মূল্যকে আবদ্ধ করে নয়।

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

যত তাড়াতাড়ি বা পরে, আপনি এই জাতীয় কোড লিখতে চলেছেন:

class Foo
{
    unique_ptr<Shape> member;

public:

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

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

একটি নামযুক্ত মূল্যসূত্র উল্লেখ অন্যান্য ভেরিয়েবলের মতো একটি লভ্যালু l

সমাধানটি ম্যানুয়ালি সরানো সক্ষম করে:

class Foo
{
    unique_ptr<Shape> member;

public:

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

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

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

বিশেষ সদস্যের কাজ

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

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

মূল্যমানের উল্লেখগুলি বিভিন্ন সংস্করণে গিয়েছিল। সংস্করণ 3.0, সি ++ 11 চাহিদা অনুযায়ী দুটি অতিরিক্ত বিশেষ সদস্য ফাংশন ঘোষণা করে: মুভ কনস্ট্রাক্টর এবং মুভ অ্যাসাইনমেন্ট অপারেটর। মনে রাখবেন যে ভিসি 10 বা ভিসি 11 এখনও 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;
}

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

ফরোয়ার্ডিং রেফারেন্স ( পূর্বে সর্বজনীন রেফারেন্স হিসাবে পরিচিত )

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

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

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

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>&

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

টি অ্যান্ড অ্যান্ড কোনও মূলসূত্র উল্লেখ নয়, তবে একটি ফরোয়ার্ডিং রেফারেন্স। এটি লভ্যালুগুলিকেও আবদ্ধ করে, এক্ষেত্রে Tএবং T&&উভয়ই ল্যাভেলু রেফারেন্স।

আপনি যদি কোনও ফাংশন টেম্পলেটকে মূল্যকে সীমাবদ্ধ করতে চান তবে আপনি 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কারণ অন্যথায়, ধরনের lvalues জন্য মেটা-ফাংশন কল প্রয়োজনীয় X, রিটার্ন টাইপ হবে X& &&, যা ভেঙ্গে হবে X&। যেহেতু tসর্বদা লভ্যালু হয় (মনে রাখবেন যে নামযুক্ত মূল্যসূচক রেফারেন্সটি একটি লভ্যালু) তবে আমরা tকোনও মূল্যবোধের রেফারেন্সকে আবদ্ধ করতে চাই , আমাদের স্পষ্টভাবে tসঠিক ফেরতের প্রকারে কাস্ট করতে হবে। কোনও ফাংশনের কল যা কোনও মূল্যের রেফারেন্স দেয় তা নিজেই একটি মূল্যবান। এখন আপনি জানেন যে কোথা থেকে কোথাও এসেছে;)

কোনও ক্রিয়াকলাপের কল যা কোনও মূল্যের রেফারেন্স প্রদান করে, যেমন std::move, একটি মূল্য x

নোট করুন যে এই উদাহরণে যথাযথ রেফারেন্স দ্বারা ফিরে আসা ঠিক আছে, কারণ tকোনও স্বয়ংক্রিয় বস্তু বোঝায় না, বরং পরিবর্তে এমন একটি বস্তু যা কলার দ্বারা প্রেরণ করা হয়েছিল।



24
তৃতীয় কারণ মুভ সিমান্টিকস গুরুত্বপূর্ণ: ব্যতিক্রম সুরক্ষা। প্রায়শই যেখানে অনুলিপি অপারেশন নিক্ষেপ করতে পারে (কারণ এটির জন্য সংস্থানগুলি বরাদ্দ করা দরকার এবং বরাদ্দ ব্যর্থ হতে পারে) একটি স্থানান্তর অপারেশন নো-থ্রো হতে পারে (কারণ এটি নতুন সংস্থান বরাদ্দের পরিবর্তে বিদ্যমান সংস্থাগুলির মালিকানা হস্তান্তর করতে পারে)। ব্যর্থ হতে পারে না এমন ক্রিয়াকলাপগুলি সর্বদা দুর্দান্ত এবং ব্যতিক্রমী গ্যারান্টি সরবরাহ করে এমন কোড লেখার সময় এটি গুরুত্বপূর্ণ হতে পারে।
ব্র্যাংডন

8
আমি 'ইউনিভার্সাল রেফারেন্স' অবধি আপনার সাথে ছিলাম, তবে তারপরে এটি অনুসরণ করা খুব বিমূর্ত। রেফারেন্স ভেঙে যাচ্ছে? পারফেক্ট ফরওয়ার্ডিং? আপনি কি বলছেন যে প্রকারটি টেম্পলেট করা হলে কোনও মূল্যবোধের রেফারেন্স সর্বজনীন রেফারেন্স হয়ে যায়? আমি ইচ্ছুক এটির ব্যাখ্যা করার মতো কোনও উপায় ছিল যাতে আমি বুঝতে পারি যে এটি বোঝার দরকার আছে কিনা আমার! :)
কাইলোটন

8
দয়া করে এখনই একটি বই লিখুন ... এই উত্তরটি আমাকে বিশ্বাস করার কারণ দিয়েছে যে আপনি যদি C ++ এর অন্য কোণগুলি এইভাবে সুদৃuc়ভাবে আবৃত করেন তবে আরও হাজার হাজার মানুষ এটি বুঝতে পারবেন।
হ্যালিভিংস্টন

12
@ লাইভিংস্টন আপনার সদয় মতামতের জন্য আপনাকে অনেক ধন্যবাদ, আমি সত্যিই এটির প্রশংসা করি। একটি বই লেখার সমস্যাটি হ'ল: আপনি সম্ভবত কল্পনা করতে পারেন তার থেকে অনেক বেশি কাজ। আপনি যদি সি ++ 11 বা তারও বেশি গভীরভাবে গভীর খনন করতে চান তবে আমি আপনাকে স্কট মায়ার্স দ্বারা "কার্যকর আধুনিক সি ++" কেনার পরামর্শ দিই।
ফ্রেডওভারফ্লো

77

মুভ শব্দার্থবিজ্ঞান মূলসূত্র উল্লেখগুলির উপর ভিত্তি করে ।
একটি মূল্যমান হ'ল একটি অস্থায়ী বস্তু, যা প্রকাশের শেষে নষ্ট হতে চলেছে। বর্তমান সি ++ এ, মূল্যবোধগুলি কেবলমাত্র constরেফারেন্সের সাথে আবদ্ধ । C ++ 1x constবানানযুক্ত অ- মূল্যবোধের রেফারেন্সগুলিকে অনুমতি দেবে 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যুক্তি একটি মূল্যবান , তাই আমরা জানি যে এটির আর কোনও প্রয়োজন নেই এবং আমরা এর মূল্য চুরি করতে পারি।
সুতরাং মানটি নামবিহীন অস্থায়ী থেকে ফিরে এসে স্থানান্তরিতf() হয় x(যখন ডেটা x, শূন্যে আরম্ভ করা Xহয়, অস্থায়ী মধ্যে সরানো হয় যা কার্যের পরে নষ্ট হয়ে যায়)।


1
মনে রাখবেন যে এটি হওয়া উচিত this->swap(std::move(rhs));কারণ নামযুক্ত মূল্যসূত্রগুলি হল লভ্যালুগুলি
wmamarak

এটি @ ভুলের মন্তব্য অনুসারে ভুল, এর প্রসঙ্গে rhsএকটি মূল্যবানX::X(X&& rhs)std::move(rhs)একটি মূল্যায়ন পেতে আপনাকে কল করতে হবে, তবে এই ধরণের উত্তরটি মোটা করে তুলবে।
আশে

বিন্দুবিহীন প্রকারের জন্য শব্দার্থবিজ্ঞান কী সরায়? শব্দার্থবিজ্ঞানের কাজ সরু কপি কাজ?
গুসেভ স্লাভা

@ গুসেভ: আপনি কী জিজ্ঞাসা করছেন তা আমার কোনও ধারণা নেই।
sbi

60

ধরুন আপনার একটি ফাংশন রয়েছে যা যথেষ্ট পরিমাণে বস্তুটি ফেরত দেয়:

Matrix multiply(const Matrix &a, const Matrix &b);

আপনি যখন এই জাতীয় কোড লিখবেন:

Matrix r = multiply(a, b);

তারপরে একটি সাধারণ সি ++ সংকলক ফলাফলের জন্য একটি অস্থায়ী অবজেক্ট তৈরি করবে multiply(), অনুলিপি করতে কপি নির্মাণকারীকে কল করবে rএবং তারপরে অস্থায়ী রিটার্ন মানটি নষ্ট করবে। সি ++ 0x এ শব্দার্থিকগুলি সরান "মুভ কনস্ট্রাক্টর" rএর বিষয়বস্তু অনুলিপি করে আরম্ভ করার জন্য ডেকে আনা হয় এবং তারপরে এটি বিনষ্ট না করে অস্থায়ী মান বাতিল করে দেয়।

এটি বিশেষত গুরুত্বপূর্ণ যদি (সম্ভবত Matrixউপরের উদাহরণের মতো ), অনুলিপি করা বস্তুটির অভ্যন্তরীণ উপস্থাপনা সংরক্ষণের জন্য গাদাতে অতিরিক্ত মেমরি বরাদ্দ করে। কোনও অনুলিপি নির্মাণকারীকে হয় অভ্যন্তরীণ উপস্থাপনার সম্পূর্ণ কপি তৈরি করতে হবে, বা রেফারেন্স গণনা এবং অনুলিপি-অনুলিপি শব্দার্থ ব্যবহার করতে হবে। একটি মুভ কনস্ট্রাক্টর হ্যাপ মেমরিটি একা রেখে দিতেন এবং কেবল পয়েন্টারটির সাথে পয়েন্টারটি অনুলিপি করতেন Matrix


2
কীভাবে মুভ কনস্ট্রাক্টর এবং কপি কনস্ট্রাক্টর আলাদা?
ডিক্রোকস

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

3
@ ডিক্রোস: একটি ফাঁকা বস্তু তৈরি করে এবং একটি অনুলিপি করে। যদি অবজেক্টে সঞ্চিত ডেটা বড় হয় তবে একটি অনুলিপি ব্যয়বহুল হতে পারে। উদাহরণস্বরূপ, std :: ভেক্টর।
বিলি ওনিল

1
@ কুঞ্জ 2aan: এটি আপনার সংকলকের উপর নির্ভর করে, আমার সন্দেহ। সংকলকটি ফাংশনের অভ্যন্তরে একটি অস্থায়ী অবজেক্ট তৈরি করতে পারে এবং তারপরে এটিকে কলারের ফেরতের মূল্যে স্থানান্তরিত করতে পারে। অথবা, এটি মুভি কনস্ট্রাক্টর ব্যবহারের প্রয়োজন ছাড়াই, প্রত্যক্ষ মানের ক্ষেত্রে অবজেক্টটি সরাসরি নির্মাণ করতে সক্ষম হতে পারে।
গ্রেগ হিউগিল

2
: @Jichao: এটা একটা অপ্টিমাইজেশান RVO বলা হয়, দেখুন পার্থক্য আরো তথ্যের জন্য এই প্রশ্ন হল stackoverflow.com/questions/5031778/...
গ্রেগ Hewgill

30

আপনি যদি মুভ শব্দার্থবিজ্ঞানের একটি ভাল, গভীর-ব্যাখ্যাতে সত্যিই আগ্রহী হন তবে আমি তাদের উপর মূল কাগজটি পড়ার জন্য সুপারিশ করব, "সি ++ ভাষাতে মুভ সেমেন্টিকস সাপোর্ট যুক্ত করার প্রস্তাব"।

এটি খুব সহজেই অ্যাক্সেসযোগ্য এবং সহজেই পড়তে পারে এবং তারা যে সুবিধা দেয় সেগুলির জন্য এটি একটি দুর্দান্ত কেস তৈরি করে। ডাব্লুজি 21 ওয়েবসাইটে মুভ শব্দার্থবিজ্ঞান সম্পর্কিত আরও আরও সাম্প্রতিক এবং সর্বশেষতম কাগজপত্র রয়েছে , তবে এটি সম্ভবত সবচেয়ে সরল যেহেতু এটি শীর্ষ-স্তরের দর্শন থেকে জিনিসগুলির নিকটে আসে এবং কৌতূহলপূর্ণ ভাষার বিবরণে খুব বেশি যায় না।


27

মুভ সিমানটিকস হ'ল সম্পদ স্থানান্তর করার পরিবর্তে যখন তাদের কারওর কাছে আর মূল্যের প্রয়োজন হয় না তখন তাদের অনুলিপি করা than

সি ++ 03 এ অবজেক্টগুলি প্রায়শই অনুলিপি করা হয়, কেবল কোনও কোডটি আবার মান ব্যবহার করার আগে কেবল ধ্বংস বা নির্ধারিত ওভার করা। উদাহরণস্বরূপ, আপনি যখন কোনও ফাংশন থেকে মূল্য দিয়ে ফিরে যান — যতক্ষণ না আরভিও কিক না করে you're আপনি যে মানটি ফিরিয়ে দিচ্ছেন তা কলারের স্ট্যাক ফ্রেমে অনুলিপি করা হয় এবং তারপরে এটি সুযোগের বাইরে চলে যায় এবং ধ্বংস হয়। এটি কেবলমাত্র কয়েকটি উদাহরণের মধ্যে একটি: উত্স-পদটি একটি অস্থায়ী হলে পাস-বাই-মান দেখুন, sortঠিক যেমন আইটেমগুলিকে পুনর্বিন্যাস করুন, এর ছাড়িয়ে vectorগেলে পুনঃনির্ধারণ capacity()ইত্যাদি etc.

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


23

সহজ (ব্যবহারিক) পদে:

কোনও বস্তু অনুলিপি করার অর্থ তার "স্থির" সদস্যদের অনুলিপি করা এবং newতার গতিশীল অবজেক্টগুলির জন্য অপারেটরকে কল করা । রাইট?

class A
{
   int i, *p;

public:
   A(const A& a) : i(a.i), p(new int(*a.p)) {}
   ~A() { delete p; }
};

তবে, এর সরানো একটি বস্তু (আমি একটি দেখুন ব্যবহারিক বিন্দুতে পুনরাবৃত্তি,) শুধুমাত্র বোঝা গতিশীল বস্তুর পয়েন্টার কপি করতে, এবং নতুন একটি তৈরি করতে না।

তবে, তা কি বিপজ্জনক নয়? অবশ্যই, আপনি দুবার একটি গতিশীল বস্তুর ধ্বংস করতে পারেন (বিভাগকরণ ত্রুটি)। সুতরাং, এড়াতে, আপনার উত্স পয়েন্টারগুলিকে দুবার ধ্বংস না করার জন্য আপনার "অবৈধ" করা উচিত:

class A
{
   int i, *p;

public:
   // Movement of an object inside a copy constructor.
   A(const A& a) : i(a.i), p(a.p)
   {
     a.p = nullptr; // pointer invalidated.
   }

   ~A() { delete p; }
   // Deleting NULL, 0 or nullptr (address 0x0) is safe. 
};

ঠিক আছে, তবে আমি যদি কোনও বস্তু সরিয়ে ফেলি তবে উত্স অবজেক্টটি অকেজো হয়ে যায়, না? অবশ্যই, তবে নির্দিষ্ট পরিস্থিতিতে এটি খুব দরকারী। সর্বাধিক স্পষ্ট একটি হ'ল আমি যখন কোনও বেনামী অবজেক্টের সাথে কোনও ফাংশন কল করি (অস্থায়ী, মূল্যবান বস্তু, ..., আপনি এটি বিভিন্ন নামে কল করতে পারেন):

void heavyFunction(HeavyType());

এই পরিস্থিতিতে, একটি বেনামে তৈরি বস্তু তৈরি করা হবে, এর পরে ফাংশন প্যারামিটারে অনুলিপি করা হবে এবং তারপরে মুছে ফেলা হবে। সুতরাং, এখানে অবজেক্টটি সরিয়ে নেওয়া ভাল, কারণ আপনার বেনামে অবজেক্টের প্রয়োজন নেই এবং আপনি সময় এবং স্মৃতি সঞ্চয় করতে পারেন।

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

class A
{
   int i, *p;

public:
   // Copy
   A(const A& a) : i(a.i), p(new int(*a.p)) {}

   // Movement (&& means "rvalue reference to")
   A(A&& a) : i(a.i), p(a.p)
   {
      a.p = nullptr;
   }

   ~A() { delete p; }
};

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

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

যদি আপনার অবজেক্ট জটিল হয় এবং ডেস্ট্রাক্টরের অন্যান্য গৌণ প্রভাব থাকে যেমন লাইব্রেরির ফাংশনটিতে কল করা, অন্যান্য বৈশ্বিক ফাংশনগুলিতে কল করা বা এটি যাই হোক না কেন, সম্ভবত একটি পতাকা সহ একটি আন্দোলনের সংকেত দেওয়া ভাল:

class Heavy
{
   bool b_moved;
   // staff

public:
   A(const A& a) { /* definition */ }
   A(A&& a) : // initialization list
   {
      a.b_moved = true;
   }

   ~A() { if (!b_moved) /* destruct object */ }
};

সুতরাং, আপনার কোডটি সংক্ষিপ্ত ( nullptrপ্রতিটি গতিশীল সদস্যের জন্য আপনাকে কোনও কাজ করার দরকার নেই ) এবং আরও সাধারণ।

অন্যান্য সাধারণ প্রশ্ন: A&&এবং এর মধ্যে পার্থক্য কী const A&&? অবশ্যই, প্রথম ক্ষেত্রে, আপনি বস্তুটি সংশোধন করতে পারেন এবং দ্বিতীয়টিতে নয়, তবে ব্যবহারিক অর্থ? দ্বিতীয় ক্ষেত্রে, আপনি এটি সংশোধন করতে পারবেন না, সুতরাং আপনার কাছে অবজেক্টটি অবৈধ করার কোনও উপায় নেই (কোনও পরিবর্তনীয় পতাকা বা এর মতো কিছু বাদে), এবং অনুলিপি নির্মাণকারীটির সাথে কোনও ব্যবহারিক পার্থক্য নেই।

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

void some_function(A&& a)
{
   other_function(a);
}

অবজেক্টটির aআসল প্যারামিটারে অনুলিপি করা হবে other_function। যদি আপনি চান যে এই অবজেক্টটিকে aঅস্থায়ী অবজেক্ট হিসাবে ধরে রাখা অব্যাহত থাকে তবে আপনার এই std::moveফাংশনটি ব্যবহার করা উচিত :

other_function(std::move(a));

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

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

template<typename T>
void some_function(T&& a)
{
   other_function(std::forward<T>(a));
}

এটি এমন এক প্রোটোটাইপিকাল ফাংশনের স্বাক্ষর যা সি ++ 11 এর মাধ্যমে প্রয়োগ করা নিখুঁত ফরোয়ার্ডিং ব্যবহার করে std::forward। এই ফাংশনটি টেমপ্লেট ইনস্ট্যান্টিশনের কিছু নিয়ম ব্যবহার করে:

 `A& && == A&`
 `A&& && == A&&`

সুতরাং, যদি Tআপনি একটি lvalue রেফারেন্স A( টি = এ &), aএছাড়াও ( এ & && => এ &)। যদি Tআপনি একটি rvalue রেফারেন্স A, aএছাড়াও (একটি && && => একটি &&)। উভয় ক্ষেত্রেই aপ্রকৃত সুযোগে একটি নামী বস্তু, তবে Tকলার স্কোপের দৃষ্টিকোণ থেকে এটির "রেফারেন্স টাইপ" সম্পর্কিত তথ্য থাকে। এই তথ্য ( T) টেমপ্লেট প্যারামিটার হিসাবে পাস করা হয় forwardএবং 'এ' সরানো হয় বা প্রকার অনুসারে নয় T


20

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


13

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

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

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

std::vector<foo> get_foos();

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


1
আমি এটি একটি দুর্দান্ত উদাহরণ বলে মনে করি না, কারণ এই ফাংশনটি রিটার্ন মান উদাহরণগুলিতে রিটার্ন মান অপ্টিমাইজেশন সম্ভবত ইতিমধ্যে অনুলিপি অপারেশন মুছে ফেলছে।
Zan Lynx

7

মুভ শব্দার্থবিজ্ঞানের প্রয়োজনীয়তা চিত্রিত করার জন্য , আসুন অর্থ-শব্দ পদার্থবিহীন এই উদাহরণটি বিবেচনা করুন:

এখানে এমন একটি ফাংশন যা টাইপের একটি অবজেক্ট নেয় 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রেফারেন্সের মতো কাজ করে :
একটি মূল্যমানের রেফারেন্স স্থানান্তরিত হতে পারে এবং একটি মূল্যমান পারে না।

সিপ্রেফারেন্স.কম থেকে :

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


7

আমি এটি সঠিকভাবে বুঝতে পেরেছি তা নিশ্চিত করার জন্য আমি এটি লিখছি।

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

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

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

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


"একটি" মুভমেন্ট অ্যাসাইনমেন্ট "প্রোগ্রামারটিকে ডিফল্ট অনুলিপি ব্যবহারের ওভাররাইড করতে দেয় এবং পরিবর্তে অবজেক্টগুলির জন্য রেফারেন্স সোপান করে, যার অর্থ হ'ল কোনও অনুলিপি নেই এবং অদলবদল অপারেশনটি আরও দ্রুত।" - এই দাবিগুলি অস্পষ্ট এবং বিভ্রান্তিকর। দুটি বস্তু অদলবদল করতে xএবং y, আপনি কেবল "বস্তুগুলির রেফারেন্স রেফারেন্স" করতে পারবেন না ; এটি হতে পারে যে অবজেক্টগুলিতে এমন পয়েন্টার রয়েছে যা অন্যান্য ডেটা উল্লেখ করে এবং সেই পয়েন্টারগুলি অদলবদল করা যায়, তবে মুভ অপারেটরদের কোনও কিছু অদলবদলের প্রয়োজন হয় না । তারা এতে স্থানটির ডেটা সংরক্ষণের পরিবর্তে স্থানান্তরিত বস্তু থেকে ডেটা মুছতে পারে।
টনি ডেলরয়

আপনি swap()চলন্ত শব্দার্থবিজ্ঞান ছাড়া লিখতে পারে । "স্ট্যান্ড অ্যাসাইনমেন্টটি স্ট্যান্ড :: মুভ () পদ্ধতিতে কল করে আহ্বান করা যেতে পারে।" - এটি কখনও কখনও ব্যবহারের প্রয়োজন হয় std::move()- যদিও এটি আসলে কোনও কিছুই সরায় না - কেবল কম্পাইলারটি জানায় যে আর্গুমেন্টটি চলমান, কখনও কখনও std::forward<>()(ফরোয়ার্ডিং রেফারেন্স সহ), এবং অন্য সময় সংকলক জানে কোনও মান সরিয়ে নেওয়া যায়।
টনি ডেলরয়

-2

বার্জন স্ট্রোস্ট্রপের "দ্য সি ++ প্রোগ্রামিং ল্যাঙ্গুয়েজ" বইয়ের একটি উত্তর এখানে রয়েছে । আপনি যদি ভিডিওটি দেখতে না চান তবে আপনি নীচের পাঠ্যটি দেখতে পারেন:

এই স্নিপেট বিবেচনা করুন। অপারেটর থেকে ফিরে আসার ক্ষেত্রে ফলাফলটি স্থানীয় ভেরিয়েবলের বাইরে এবং এমন কোনও resজায়গায় কপি করা জড়িত যেখানে কলার এটি অ্যাক্সেস করতে পারে।

Vector operator+(const Vector& a, const Vector& b)
{
    if (a.size()!=b.size())
        throw Vector_siz e_mismatch{};
    Vector res(a.size());
        for (int i=0; i!=a.size(); ++i)
            res[i]=a[i]+b[i];
    return res;
}

আমরা আসলে একটি অনুলিপি চাইনি; আমরা কেবল একটি ফাংশন থেকে ফলাফল পেতে চেয়েছিলেন। সুতরাং আমাদের এটির অনুলিপি করার পরিবর্তে ভেক্টর সরিয়ে নেওয়া দরকার। আমরা নিম্নরূপে মুভ কনস্ট্রাক্টর সংজ্ঞায়িত করতে পারি:

class Vector {
    // ...
    Vector(const Vector& a); // copy constructor
    Vector& operator=(const Vector& a); // copy assignment
    Vector(Vector&& a); // move constructor
    Vector& operator=(Vector&& a); // move assignment
};

Vector::Vector(Vector&& a)
    :elem{a.elem}, // "grab the elements" from a
    sz{a.sz}
{
    a.elem = nullptr; // now a has no elements
    a.sz = 0;
}

&& এর অর্থ "মূল্যবোধের রেফারেন্স" এবং এটি এমন একটি উল্লেখ যা আমরা কোনও মূল্যকে বাঁধতে পারি। "মূল্য" "এর অর্থ" ল্যাভেলু "পরিপূরক করা যার অর্থ মোটামুটি অর্থ" এমন কিছু যা একটি কার্যভারের বাম দিকে প্রদর্শিত হতে পারে। " সুতরাং একটি মূল্যের অর্থ মোটামুটি "একটি মান যা আপনি বরাদ্দ করতে পারবেন না" যেমন কোনও ফাংশন কল দ্বারা একটি পূর্ণসংখ্যা এবং resভেক্টরগুলির জন্য অপারেটর + () এর স্থানীয় ভেরিয়েবল।

এখন, বিবৃতি return res;কপি করবে না!

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.