আমার প্রথম উত্তরটি শব্দার্থবিজ্ঞান সরিয়ে নেওয়ার জন্য একটি অত্যন্ত সরল ভূমিকা ছিল এবং এটিকে সহজ রাখার লক্ষ্যে অনেকগুলি বিবরণ উদ্দেশ্যতে রেখে দেওয়া হয়েছিল। যাইহোক, শব্দার্থবিজ্ঞান সরানোর জন্য আরও অনেক কিছু রয়েছে এবং আমি ভেবেছিলাম ফাঁক পূরণ করার জন্য এটি দ্বিতীয় উত্তরের সময় হয়ে গেছে। প্রথম উত্তরটি ইতিমধ্যে বেশ পুরানো, এবং একে একে সম্পূর্ণ আলাদা পাঠ্যের সাহায্যে প্রতিস্থাপন করা ঠিক মনে হয়নি। আমার মনে হয় এটি এখনও প্রথম ভূমিকা হিসাবে ভাল কাজ করে। তবে আপনি যদি আরও গভীর খনন করতে চান তবে পড়ুন :)
স্টিফান টি লাভেভেজ মূল্যবান মতামত দেওয়ার জন্য সময় নিয়েছিলেন। আপনাকে অনেক ধন্যবাদ, স্টিফান!
ভূমিকা
মুভ সিমানটিকস কোনও বস্তুকে কিছু শর্তের অধীনে অন্য কোনও বস্তুর বাহ্যিক সংস্থার মালিকানা নিতে দেয়। এটি দুটি উপায়ে গুরুত্বপূর্ণ:
ব্যয়বহুল অনুলিপিগুলিকে সস্তা চালগুলিতে পরিণত করা। একটি উদাহরণের জন্য আমার প্রথম উত্তর দেখুন। মনে রাখবেন যে কোনও বস্তু যদি কমপক্ষে একটি বাহ্যিক সংস্থান পরিচালনা করে না (প্রত্যক্ষ বা অপ্রত্যক্ষভাবে তার সদস্য বস্তুর মাধ্যমে), সরানো শব্দার্থিকগুলি অনুলিপি শব্দার্থবিজ্ঞানের তুলনায় কোনও সুবিধা দেয় না। সেক্ষেত্রে কোনও বস্তু অনুলিপি করা এবং কোনও বস্তুকে সরিয়ে নেওয়া মানে হুবহু একই জিনিস:
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
// ...
};
নিরাপদ "কেবলমাত্র সরানো" প্রকারের প্রয়োগ করা; এটি হল, প্রকারের জন্য অনুলিপি করা কোনও অর্থবোধ করে না, তবে সরানো যায় না। উদাহরণগুলির মধ্যে রয়েছে লক, ফাইল হ্যান্ডলগুলি এবং স্বতন্ত্র মালিকানা শব্দার্থবিজ্ঞান সহ স্মার্ট পয়েন্টার। দ্রষ্টব্য: এই উত্তরটি আলোচনা করে 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
করার X
। X
সেক্ষেত্রে টাইপের একটি অস্থায়ী তৈরি করা হয় এবং মূলসূত্র উল্লেখটি অস্থায়ীভাবে আবদ্ধ হয়:
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
আরম্ভের পরে আর ব্যবহার করা হয় না member
। std::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
কোনও স্বয়ংক্রিয় বস্তু বোঝায় না, বরং পরিবর্তে এমন একটি বস্তু যা কলার দ্বারা প্রেরণ করা হয়েছিল।