এটা যে সঠিক std::move(x)
আরো নির্দিষ্টভাবে একটি করুন - হয় rvalue মাত্র একটি ঢালাই xvalue , একটি উল্টোদিকে prvalue । এবং এটিও সত্য যে নামকরণ করা কাস্ট move
কখনও কখনও মানুষকে বিভ্রান্ত করে। তবে এই নামকরণের উদ্দেশ্যটি বিভ্রান্ত করা নয়, বরং আপনার কোডটি আরও পাঠযোগ্য।
ইতিহাস 2002 সালে মূল পদক্ষেপ প্রস্তাব থেকেmove
ফিরে । এই কাগজটি প্রথমে মূলতার রেফারেন্সের সাথে পরিচয় করিয়ে দেয় এবং তারপরে আরও কার্যকরভাবে কীভাবে লিখতে হয় তা দেখায় :std::swap
template <class T>
void
swap(T& a, T& b)
{
T tmp(static_cast<T&&>(a));
a = static_cast<T&&>(b);
b = static_cast<T&&>(tmp);
}
এক স্মরণ যে ইতিহাসে এই সময়ে, একমাত্র জিনিস যে "হয়েছে &&
" পারে সম্ভবত গড় ছিল যৌক্তিক এবং । মূল্যমানের রেফারেন্সগুলির সাথে কেউ পরিচিত ছিল না, বা কোনও মূল্যকে কোনও মূল্য দেওয়ার জন্য (যেমনটি কোনও অনুলিপি তৈরি করার সময় নয় static_cast<T>(t)
) জড়িত সম্পর্কেও জানত না । সুতরাং এই কোডটির পাঠকরা স্বাভাবিকভাবেই ভাবেন:
আমি জানি কীভাবে swap
কাজ করার কথা রয়েছে (অস্থায়ীভাবে অনুলিপি করুন এবং তারপরে মানগুলি বিনিময় করুন) তবে এই কুৎসিত জাতগুলির উদ্দেশ্য কী ?!
এটিও নোট করুন যে swap
সব ধরণের ক্রম-পরিবর্তন-সংশোধনকারী অ্যালগরিদমগুলির জন্য সত্যই একটি স্ট্যান্ড-ইন। এই আলোচনা অনেক বেশি , অনেক বড় swap
।
তারপরে প্রস্তাবটি সিনট্যাক্স চিনির সাথে পরিচয় করিয়ে দিয়েছিল যা static_cast<T&&>
আরও বেশি পাঠযোগ্য কিছু দিয়ে প্রতিস্থাপন করে যা সুনির্দিষ্ট কোনটি বোঝায় না , বরং কেন :
template <class T>
void
swap(T& a, T& b)
{
T tmp(move(a));
a = move(b);
b = move(tmp);
}
অর্থাত move
মাত্র বাক্য গঠন চিনি হয় static_cast<T&&>
, এবং এখন কোডে কেন কাস্ট আছে যেমন বেশ ইঙ্গিতপূর্ণ হল: পদক্ষেপ শব্দার্থবিদ্যা সক্ষম করুন!
একজনকে অবশ্যই বুঝতে হবে যে ইতিহাসের প্রেক্ষাপটে, এই মুহুর্তে খুব কম লোকই মূল্যের এবং সরানো শব্দার্থবিজ্ঞানের মধ্যে অন্তরঙ্গ সংযোগটি বুঝতে পেরেছিল (যদিও কাগজটি সেটিকেও ব্যাখ্যা করার চেষ্টা করেছে):
মূল আর্গুমেন্টগুলি দেওয়া হলে মুভ শব্দার্থকগুলি স্বয়ংক্রিয়ভাবে খেলায় আসবে। এটি পুরোপুরি নিরাপদ কারণ কোনও প্রোগ্রামের বাকী অংশগুলির দ্বারা কোনও মূল্যমানের সঞ্চারিত সংস্থানগুলি লক্ষ্য করা যায় না ( কোনও পার্থক্য সনাক্ত করার জন্য অন্য কারও পক্ষে মূল্যবোধের উল্লেখ নেই )।
যদি সেই সময়ে swap
পরিবর্তে এটি উপস্থাপন করা হত:
template <class T>
void
swap(T& a, T& b)
{
T tmp(cast_to_rvalue(a));
a = cast_to_rvalue(b);
b = cast_to_rvalue(tmp);
}
তাহলে লোকেরা সেদিকে তাকাবে এবং বলে:
তবে আপনি কেন মূল্যায়ন করছেন?
মূলকথা:
যেমনটি ছিল, ব্যবহার করে move
, কেউ কখনও জিজ্ঞাসা করেনি:
তবে আপনি কেন নড়াচড়া করছেন?
বছরগুলি চলতে থাকায় এবং প্রস্তাবটি পরিমার্জনিত হওয়ার সাথে সাথে আজ আমাদের মূল্যমানের বিভাগগুলিতে মূল্যমান এবং মূল্যমানের ধারণাগুলি পরিমার্জন করা হয়েছিল :
(নির্লজ্জভাবে চিত্রটি নির্লজ্জভাবে চুরি করা হয়েছে )
তাই আজ, আমরা চেয়েছিলাম যদি swap
অবিকল বলতে কি এরকম হয়, পরিবর্তে কেন , এটি আরো মত হওয়া উচিত:
template <class T>
void
swap(T& a, T& b)
{
T tmp(set_value_category_to_xvalue(a));
a = set_value_category_to_xvalue(b);
b = set_value_category_to_xvalue(tmp);
}
এবং প্রত্যেকের নিজেরাই যে প্রশ্নটি জিজ্ঞাসা করা উচিত তা হ'ল উপরের কোডটি কম বা কম পাঠযোগ্য:
template <class T>
void
swap(T& a, T& b)
{
T tmp(move(a));
a = move(b);
b = move(tmp);
}
বা এমনকি মূল:
template <class T>
void
swap(T& a, T& b)
{
T tmp(static_cast<T&&>(a));
a = static_cast<T&&>(b);
b = static_cast<T&&>(tmp);
}
যে কোনও ইভেন্টে, ট্র্যাভেলম্যান সি ++ প্রোগ্রামারটি জেনে রাখা উচিত যে move
কাস্টিং ছাড়া আর কিছুই চলছে না। এবং শিক্ষানবিশ সি ++ প্রোগ্রামার, কমপক্ষে সাথে move
জানানো হবে যে আরএইচএস থেকে অনুলিপি করার বিপরীতে আরএইচ থেকে সরানো অভিপ্রায় , যদিও তারা ঠিক কীভাবে এটি সম্পন্ন হয়েছে তা বুঝতে না পারলেও ।
অধিকন্তু, যদি কোনও প্রোগ্রামার অন্য নামে এই কার্যকারিতাটি চান, তবে এই কার্যকারিতাটিতে std::move
কোনও একচেটিয়া রাখেন না এবং এর প্রয়োগে কোনও বহনযোগ্য ভাষা জাদু নেই। উদাহরণস্বরূপ যদি কেউ কোড করতে চায় set_value_category_to_xvalue
এবং পরিবর্তে এটি ব্যবহার করে তবে এটি করা তুচ্ছ is
template <class T>
inline
constexpr
typename std::remove_reference<T>::type&&
set_value_category_to_xvalue(T&& t) noexcept
{
return static_cast<typename std::remove_reference<T>::type&&>(t);
}
সি ++ ১৪ এ এটি আরও সংক্ষিপ্ত আকার ধারণ করে:
template <class T>
inline
constexpr
auto&&
set_value_category_to_xvalue(T&& t) noexcept
{
return static_cast<std::remove_reference_t<T>&&>(t);
}
সুতরাং যদি আপনি এত ঝুঁকে থাকেন static_cast<T&&>
তবে আপনার সবচেয়ে ভাল মনে করেন এমনভাবে সাজান এবং সম্ভবত আপনি একটি নতুন সেরা অনুশীলনের বিকাশ করবেন (সি ++ ক্রমাগত বিকশিত হচ্ছে)।
সুতরাং move
জেনারেটেড অবজেক্ট কোডের ক্ষেত্রে কী করবেন?
এটি বিবেচনা করুন test
:
void
test(int& i, int& j)
{
i = j;
}
সংকলিত clang++ -std=c++14 test.cpp -O3 -S
, এটি এই অবজেক্ট কোড উত্পাদন করে:
__Z4testRiS_: ## @_Z4testRiS_
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp0:
.cfi_def_cfa_offset 16
Ltmp1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp2:
.cfi_def_cfa_register %rbp
movl (%rsi), %eax
movl %eax, (%rdi)
popq %rbp
retq
.cfi_endproc
এখন যদি পরীক্ষাটি পরিবর্তন করা হয়:
void
test(int& i, int& j)
{
i = std::move(j);
}
নেই একেবারে সব সময়ে কোন পরিবর্তন বস্তুর কোডে। এই ফলাফলটিকে সাধারণীকরণ করা যায়: তুচ্ছভাবে চলমান বস্তুর জন্য, std::move
এর কোনও প্রভাব নেই।
এখন এই উদাহরণটি দেখুন:
struct X
{
X& operator=(const X&);
};
void
test(X& i, X& j)
{
i = j;
}
এটি উত্পন্ন করে:
__Z4testR1XS0_: ## @_Z4testR1XS0_
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp0:
.cfi_def_cfa_offset 16
Ltmp1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp2:
.cfi_def_cfa_register %rbp
popq %rbp
jmp __ZN1XaSERKS_ ## TAILCALL
.cfi_endproc
আপনি চালাতে পারেন __ZN1XaSERKS_
মাধ্যমে c++filt
এটা সৃষ্টি করে: X::operator=(X const&)
। এখানে অবাক হওয়ার কিছু নেই। এখন যদি পরীক্ষাটি পরিবর্তন করা হয়:
void
test(X& i, X& j)
{
i = std::move(j);
}
তারপরে উত্পন্ন অবজেক্ট কোডটিতে এখনও কোনও পরিবর্তন নেই । কোনও মূল্যবোধে std::move
ফেলে j
দেওয়া ছাড়া আর কিছুই করেনি, এবং তারপরে সেই মূল্যটি X
অনুলিপিটির অনুলিপি অপারেটরের সাথে আবদ্ধ X
।
এখন এগুলিতে একটি সরানো নিয়োগ অপারেটর যুক্ত করতে দিন X
:
struct X
{
X& operator=(const X&);
X& operator=(X&&);
};
এখন অবজেক্ট কোড আছে পরিবর্তন:
__Z4testR1XS0_: ## @_Z4testR1XS0_
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp0:
.cfi_def_cfa_offset 16
Ltmp1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp2:
.cfi_def_cfa_register %rbp
popq %rbp
jmp __ZN1XaSEOS_ ## TAILCALL
.cfi_endproc
চলমান __ZN1XaSEOS_
মাধ্যমে c++filt
জানায় যে X::operator=(X&&)
পরিবর্তে বলা হচ্ছে X::operator=(X const&)
।
আর এটাই তো আছে std::move
! এটি রান সময়ে পুরোপুরি অদৃশ্য হয়ে যায়। এর একমাত্র প্রভাবটি সংকলন-সময়ে হয় যেখানে এটি ওভারলোডের কলটি পরিবর্তিত করতে পারে ।
std::move
আসলে চলে যায় ..