මගේ පළමු පිළිතුර අර්ථ නිරූපණය සඳහා අතිශය සරල හැඳින්වීමක් වූ අතර එය සරල ලෙස තබා ගැනීම සඳහා බොහෝ තොරතුරු අතහැර දමා ඇත. කෙසේ වෙතත්, අර්ථකථන ගෙනයාමට තවත් බොහෝ දේ ඇති අතර, හිඩැස් පිරවීම සඳහා දෙවන පිළිතුර සඳහා කාලය මෙය යැයි මම සිතුවෙමි. පළමු පිළිතුර දැනටමත් තරමක් පැරණි වන අතර එය සම්පූර්ණයෙන්ම වෙනස් පා with යකින් ආදේශ කිරීම නිවැරදි යැයි හැඟුනේ නැත. මම හිතන්නේ එය තවමත් පළමු හැඳින්වීමක් ලෙස සේවය කරයි. නමුත් ඔබට ගැඹුරට හෑරීමට අවශ්ය නම්, කියවන්න :)
වටිනා ප්රතිපෝෂණ ලබා දීමට ස්ටීවන් ටී. බොහොම ස්තූතියි, ස්ටීවන්!
හැදින්වීම
චලනය වන අර්ථ නිරූපණය මඟින් යම් වස්තුවකට, යම් යම් කොන්දේසි යටතේ වෙනත් වස්තුවක බාහිර සම්පත් වල අයිතිය ලබා ගැනීමට ඉඩ ලබා දේ. මෙය ආකාර දෙකකින් වැදගත් වේ:
මිල අධික පිටපත් ලාභ චලනයන් බවට පත් කිරීම. උදාහරණයක් සඳහා මගේ පළමු පිළිතුර බලන්න. වස්තුවක් අවම වශයෙන් එක් බාහිර සම්පතක් වත් කළමනාකරණය නොකරන්නේ නම් (සෘජුව හෝ වක්රව එහි සාමාජික වස්තූන් හරහා), චලනය වන අර්ථ නිරූපණයන් පිටපත් අර්ථ නිරූපණයට වඩා කිසිදු වාසියක් ලබා නොදෙන බව සලකන්න. එවැනි අවස්ථාවකදී, වස්තුවක් පිටපත් කිරීම සහ වස්තුවක් චලනය කිරීම යන්නෙන් අදහස් වන්නේ හරියටම එකම දෙයයි:
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
, අතහැර දැමූ C ++ 98 සම්මත පුස්තකාල අච්චුවක් වන අතර එය std::unique_ptr
C ++ 11 මගින් ප්රතිස්ථාපනය විය. අතරමැදි සී ++ ක්රමලේඛකයින් අවම වශයෙන් තරමක් හුරු පුරුදු වී ඇති අතර std::auto_ptr
, එය පෙන්වන “චලන අර්ථ නිරූපණය” නිසා, සී ++ 11 හි චලන අර්ථකථන සාකච්ඡා කිරීම සඳහා හොඳ ආරම්භක ස්ථානයක් සේ පෙනේ. වයි.එම්.එම්.වී.
පියවරක් යනු කුමක්ද?
සී ++ 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()
ප්රතිලාභයක් ලබා දෙන ශ්රිතයක ඇමතුම නිරූපණය කරන ප්රකාශනය අතර යම් ප්රබල වෙනසක් තිබිය යුතුය . ක උදාහරණයක් වේ lvalue , එනමුදු ක උදාහරණයක් වේ rvalue .auto_ptr
auto_ptr
a
make_triangle()
අවිනිශ්චිත හැසිරීම් වලට අවතීර්ණ වෙමින් a
අපට පසුව සාමාජික ශ්රිතයක් ඇමතීමට උත්සාහ කළ හැකි නිසා වැනි අගයන්ගෙන් ගමන් කිරීම භයානක ය a
. අනෙක් අතට, make_triangle()
පිටපත් ඉදිකිරීම්කරු සිය කාර්යය ඉටු කිරීමෙන් පසුව, අපට නැවත තාවකාලික භාවිතා කළ නොහැකි බැවින්, පරමාදර්ශ වලින් ගමන් කිරීම පරිපූර්ණ ආරක්ෂිත වේ. තාවකාලික යැයි කියනු ලබන ප්රකාශනයක් නොමැත; අපි make_triangle()
නැවත ලිවුවහොත්, අපට වෙනස් තාවකාලිකයක් ලැබේ. ඇත්ත වශයෙන්ම, තාවකාලික සිට මාරු කිරීම දැනටමත් ඊළඟ පේළියට ගොස් ඇත:
auto_ptr<Shape> c(make_triangle());
^ the moved-from temporary dies right here
අක්ෂර l
සහ r
පැවරුමක වම් පස සහ දකුණු පසින් histor තිහාසික සම්භවයක් ඇති බව සලකන්න . C ++ හි මෙය තවදුරටත් සත්ය නොවේ, මන්ද පැවරුමක වම් පසින් නොපෙන්වන අගයන් ඇත (පැවරුම් ක්රියාකරුවෙකු නොමැතිව අරා හෝ පරිශීලක අර්ථ දක්වන ලද වර්ග වැනි), සහ හැකි අගයන් ඇත (පන්ති වර්ගවල සියලු අගයන් පැවරුම් ක්රියාකරු සමඟ).
පංති වර්ගයේ අගයක් යනු තාවකාලික වස්තුවක් නිර්මාණය කරන ප්රකාශනයකි. සාමාන්ය තත්වයන් යටතේ, එකම විෂය පථය තුළ වෙනත් ප්රකාශනයක් එකම තාවකාලික වස්තුවක් දක්වන්නේ නැත.
අගය යොමු කිරීම්
Lvalues වලින් ගමන් කිරීම භයානක විය හැකි බව අපි දැන් තේරුම් ගෙන ඇත්තෙමු, නමුත් අගයන්ගෙන් ගමන් කිරීම හානිකර නොවේ. Lvalue තර්ක rvalue තර්ක වලින් වෙන්කර හඳුනා ගැනීමට C ++ ට භාෂා සහය තිබේ නම්, අපට lvalues වලින් ගමන් කිරීම සම්පූර්ණයෙන්ම තහනම් කළ හැකිය, නැතහොත් අවම වශයෙන් ඇමතුම් අඩවියේ පැහැදිලි lvalues වලින් ගමන් කිරීම වළක්වා ගත හැකිය , එවිට අපට තවදුරටත් අහම්බෙන් ගමන් නොකෙරේ.
මෙම ගැටලුවට C ++ 11 ගේ පිළිතුර rvalue යොමු කිරීම් වේ. Rvalue යොමු කිරීම යනු නව ආකාරයක යොමු කිරීමක් වන අතර එය අගයන්ට පමණක් බැඳී ඇති අතර වාක්ය ඛණ්ඩය වේ X&&
. හොඳ පැරණි සඳහන X&
දැන් හැඳින්වෙන්නේ lvalue යොමු කිරීමක් ලෙස ය . (සටහන බව X&&
ය නොවන සැඳහුම සඳහා යොමු; C ++ දෙයක් නැත.)
අපි const
මිශ්රණයට විසි කරන්නේ නම් , අපට දැනටමත් විවිධ ආකාරයේ යොමු කිරීම් හතරක් ඇත. X
ඔවුන්ට බැඳිය හැක්කේ කුමන ආකාරයේ ප්රකාශනද ?
lvalue const lvalue rvalue const rvalue
---------------------------------------------------------
X& yes
const X& yes yes yes yes
X&& yes
const X&& yes yes
ප්රායෝගිකව, ඔබට අමතක කළ හැකිය const X&&
. අගයන්ගෙන් කියවීම සීමා කිරීම එතරම් ප්රයෝජනවත් නොවේ.
Rvalue යොමු කිරීම X&&
යනු නව ආකාරයේ යොමු කිරීමක් වන අතර එය අගයන්ට පමණක් බැඳී ඇත.
ව්යාජ පරිවර්තනයන්
අගය යොමු කිරීම් අනුවාද කිහිපයක් හරහා ගමන් කළේය. 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
, වර්ගය තාවකාලික std::string
නිර්මාණය වන අතර, r
එම තාවකාලික කිරීමට බැඳී ඇත. අගයන් (ප්රකාශන) සහ තාවකාලික (වස්තූන්) අතර වෙනස මඳක් බොඳ වී ඇති එක් අවස්ථාව මෙයයි.
ඉදිකිරීම්කරුවන් ගෙනයන්න
X&&
පරාමිතියක් සහිත ශ්රිතයක් සඳහා ප්රයෝජනවත් උදාහරණයක් වන්නේ චලන ඉදිකිරීම්කරු ය X::X(X&& source)
. කළමනාකරණ සම්පතෙහි හිමිකාරිත්වය ප්රභවයෙන් වත්මන් වස්තුවට මාරු කිරීම එහි අරමුණයි.
C ++ 11 හි, 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;
}
};
චලන පැවරුම් ක්රියාකරුගේ මෙම ක්රියාවට නැංවීම මඟින් ඩිස්ට්රැක්ටරයේ සහ චලන ඉදිකිරීම්කරුගේ තර්කනය අනුපිටපත් කරයි. පිටපත් හා හුවමාරු මෝඩකම ගැන ඔබ හුරුපුරුදුද? චලනය හා මාරු කිරීමේ මෝඩකම ලෙස අර්ථ නිරූපණයන් සඳහා ද එය යෙදිය හැකිය:
unique_ptr& operator=(unique_ptr source) // note the missing reference
{
std::swap(ptr, source.ptr);
return *this;
}
};
දැන් source
එය විචල්ය වර්ගයකි unique_ptr
, එය චලනය වන ඉදිකිරීම්කරු විසින් ආරම්භ කරනු ඇත; එනම්, තර්කය පරාමිතිය වෙත ගෙන යනු ලැබේ. චලනය තවමත් සාදන්නාට rvalue යොමු පරාමිතියක් ඇති බැවින් තර්කය තවමත් අගයක් විය යුතුය. පාලන ප්රවාහය අවසන් වරහනට ළඟා වූ විට operator=
, source
විෂය පථයෙන් බැහැරව පැරණි සම්පත ස්වයංක්රීයව මුදා හරිනු ඇත.
චලනය වන පැවරුම් ක්රියාකරු විසින් කළමනාකරණය කරන ලද සම්පතක හිමිකාරිත්වය වත්මන් වස්තුවට මාරු කරමින් පැරණි සම්පත මුදා හරිනු ලැබේ. චලනය හා හුවමාරු මෝඩය ක්රියාත්මක කිරීම සරල කරයි.
Lvalues වලින් ගමන් කිරීම
සමහර විට, අපට lvalues වලින් ගමන් කිරීමට අවශ්යය. I.e. මෙම කාර්යය සඳහා, C ++ 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
තවදුරටත් ඒ ගැන තැකීමක් නොකරමි a
.
std::move(some_lvalue)
අගයකට අගයක් දක්වයි, එමඟින් පසුකාලීනව ගමන් කිරීමට හැකි වේ.
Xvalues
වුවත් ඒ සටහන std::move(a)
ක rvalue වන අතර, එහි තක්සේරුව කුමක්ද නොවන තාවකාලික වස්තුව නිර්මාණය කරන්න. මෙම සම්මුතිය කමිටුවට තෙවන වටිනාකම් කාණ්ඩයක් හඳුන්වා දීමට බල කළේය. සාම්ප්රදායික අර්ථයෙන් එය අගයක් නොවූවත්, අගය යොමු කිරීමකට බැඳිය හැකි දෙයක් xvalue (eXpiring අගය) ලෙස හැඳින්වේ . සාම්ප්රදායික අගයන් ප්රවුලූස් (පිරිසිදු අගයන්) ලෙස නම් කරන ලදී .
අගයන් සහ xvalues යන දෙකම අගයන් වේ. Xvalues සහ lvalues යන දෙකම glvalues (සාමාන්යකරණය කළ lvalues) වේ. රූප සටහනක් සමඟ සම්බන්ධතා ග්රහණය කර ගැනීම පහසුය:
expressions
/ \
/ \
/ \
glvalues rvalues
/ \ / \
/ \ / \
/ \ / \
lvalues xvalues prvalues
Xvalues පමණක් සැබවින්ම අලුත් බව සලකන්න; ඉතිරිය නැවත නම් කිරීම සහ කණ්ඩායම් කිරීම නිසා ය.
C ++ 98 rvalues C ++ 11 හි prvalues ලෙස හැඳින්වේ. පෙර ඡේදවල ඇති "rvalue" හි සියලු සිදුවීම් මානසිකව ප්රතිස්ථාපනය කරන්න.
කාර්යයන් වලින් ඉවත් වීම
මෙතෙක්, අපි දේශීය විචල්යයන් සහ ක්රියාකාරී පරාමිතීන් වෙත චලනය වීම දැක ඇත්තෙමු. නමුත් චලනය ද ප්රතිවිරුද්ධ දිශාවට කළ හැකිය. ශ්රිතයක් අගය අනුව නැවත පැමිණේ නම්, ඇමතුම් අඩවියේ ඇති යම් වස්තුවක් (බොහෝ විට දේශීය විචල්යයක් හෝ තාවකාලික, නමුත් ඕනෑම ආකාරයක වස්තුවක් විය හැකිය) ප්රකාශයෙන් පසුව ප්රකාශනය සමඟ ආරම්භ 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
තවදුරටත් නොපවතී! එම හේතුව නිසා, C ++ 11 හි විශේෂ රීතියක් ඇති අතර එය ස්වයංක්රීය වස්තු ලිවීමකින් තොරව ශ්රිත වලින් ආපසු ලබා දේ std::move
. ඇත්ත වශයෙන්ම, ස්වයංක්රීය වස්තූන් ශ්රිත වලින් ඉවතට ගෙනයාමට ඔබ කිසි විටෙකත් භාවිතා නොකළ යුතුය std::move
, මෙය “නම් කරන ලද ප්රතිලාභ අගය ප්රශස්තිකරණය” (NRVO) වළක්වයි.
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!
}
Rvalue යොමු කිරීම මඟින් කිසි විටෙකත් ස්වයංක්රීය වස්තු ආපසු එවන්න. චලනය std::move
තනිකරම සිදු කරනු ලබන්නේ චලන ඉදිකිරීම්කරු විසින් මිස හුදෙක් තක්සේරු අගයකට තක්සේරු කිරීමකින් නොවේ.
සාමාජිකයන් වෙත ගමන් කිරීම
වැඩි කල් යන්නට මත්තෙන්, ඔබ මේ ආකාරයට කේත ලිවීමට යන්නේ:
class Foo
{
unique_ptr<Shape> member;
public:
Foo(unique_ptr<Shape>&& parameter)
: member(parameter) // error
{}
};
මූලික වශයෙන්, සම්පාදකයා පැමිණිලි parameter
කරනු ඇත. ඔබ එහි වර්ගය දෙස බැලුවහොත්, ඔබට අගය යොමු දැක්වීමක් පෙනේ, නමුත් අගය යොමු කිරීමක් යනු හුදෙක් "අගයකට බැඳී ඇති යොමු කිරීමක්" යන්නයි; එහි අර්ථය ම අගයක් බව එයින් අදහස් නොවේ ! ඇත්ත වශයෙන්ම, parameter
නමක් සහිත සාමාන්ය විචල්යයකි. parameter
ඉදිකිරීම්කරුගේ ශරීරය තුළ ඔබට කැමති වාර ගණනක් භාවිතා කළ හැකි අතර, එය සෑම විටම එකම වස්තුවක් දක්වයි. එයින් ව්යංගයෙන් ගමන් කිරීම භයානක වනු ඇත, එබැවින් භාෂාව එය තහනම් කරයි.
නම් කරන ලද rvalue යොමු කිරීම වෙනත් ඕනෑම විචල්යයක් මෙන් lvalue වේ.
විසඳුම වන්නේ පියවර අතින් සක්රීය කිරීමයි:
class Foo
{
unique_ptr<Shape> member;
public:
Foo(unique_ptr<Shape>&& parameter)
: member(std::move(parameter)) // note the std::move
{}
};
parameter
ආරම්භ කිරීමෙන් පසුව එය තවදුරටත් භාවිතා නොවන බව ඔබට තර්ක කළ හැකිය member
. std::move
ප්රතිලාභ අගයන් මෙන් නිහ ly ව ඇතුළු කිරීමට විශේෂ රීතියක් නැත්තේ ඇයි ? බොහෝ විට එය සම්පාදක ක්රියාත්මක කරන්නන්ට අධික බරක් වනු ඇත. උදාහරණයක් ලෙස, ඉදිකිරීම්කරුගේ ශරීරය වෙනත් පරිවර්තන ඒකකයක තිබේ නම් කුමක් කළ යුතුද? ඊට හාත්පසින්ම වෙනස්ව, ප්රතිලාභ අගය රීතිය මඟින් සංකේත වගු පරික්ෂා කර බැලිය යුතුය return
.
ඔබට ද parameter
අගය අනුව සමත් විය හැකිය . වැනි චලනය සඳහා පමණක් වන වර්ග සඳහා unique_ptr
, තවම ස්ථාපිත මෝඩකමක් නොමැති බව පෙනේ. පුද්ගලිකව, මම අගය අනුව සම්මත වීමට කැමැත්තෙමි, මන්ද එය අතුරු මුහුණතේ අඩු කැළඹීමක් ඇති කරයි.
විශේෂ සාමාජික කාර්යයන්
C ++ 98 ව්යංගයෙන් ඉල්ලන්නේ විශේෂ සාමාජික කාර්යයන් තුනක්, එනම් ඒවා කොතැනක හෝ අවශ්ය වූ විට ය: පිටපත් සාදන්නා, පිටපත් පැවරුම් ක්රියාකරු සහ විනාශ කරන්නා ය.
X::X(const X&); // copy constructor
X& X::operator=(const X&); // copy assignment operator
X::~X(); // destructor
අගය යොමු කිරීම් අනුවාද කිහිපයක් හරහා ගමන් කළේය. 3.0 අනුවාදයේ සිට, C ++ 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;
}
මේ ආකාරයෙන්, පහේ සිට හතර දක්වා පහත වැටීම් ක්රියාත්මක කිරීම සඳහා විශේෂ සාමාජිකයින්ගේ ගණන. ව්යතිරේකය-ආරක්ෂාව සහ කාර්යක්ෂමතාව අතර හුවමාරුවක් මෙහි ඇත, නමුත් මම මෙම ගැටලුව පිළිබඳ විශේෂ expert යෙක් නොවෙමි.
යොමු යොමු යොමු කිරීම ( මීට පෙර විශ්ව යොමු ලෙස හැඳින්විණි )
පහත සඳහන් ක්රියාකාරී අච්චුව සලකා බලන්න:
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>&
තර්කය වර්ගයේ අගයක් නම් X
, T
එය අඩු කරනු ලැබේ X
, එබැවින් T&&
අදහස් X&&
වේ. ඕනෑම අයෙකු අපේක්ෂා කරන්නේ මෙයයි. නමුත් X
විශේෂ රීතියක් නිසා තර්කය වර්ගයේ අගයක් නම් , T
එය අඩු කරනු ලැබේ X&
, එබැවින් T&&
එයින් අදහස් වන්නේ එවැනි දෙයක් X& &&
. නමුත් C ++ තවමත් යොමු කිරීමට යොමු කිසිම සඳහනක් කර ඇති බැවින්, වර්ගය X& &&
වේ බිඳ බවට X&
. මෙය මුලදී ව්යාකූල සහ නිෂ් less ල බවක් පෙනෙන්නට තිබුණද, පරිපූර්ණ ඉදිරියට යැවීම සඳහා යොමු බිඳවැටීම අත්යවශ්ය වේ (ඒවා මෙහි සාකච්ඡා නොකරනු ඇත).
ටී ඇන්ඩ් ඇන්ඩ් යනු අගය යොමු කිරීමක් නොව ඉදිරියට යොමු කිරීමකි. එය lvalues සමඟ බැඳී ඇති අතර, එම අවස්ථාවේ දී 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
එසේ නැත්නම්, වර්ගයේ lvalues සඳහා නිසා පාර-උත්සවය ඇමතුමක් අවශ්ය X
, නැවත වර්ගය වනු ඇත X& &&
, බවට බිඳ වැටෙනු ඇතැයි වන X&
. සිට t
හැම විටම lvalue වේ (අ නම් rvalue යොමු වූ lvalue වන බව මතක තබා ගන්න), නමුත් අපි බැඳීමට අවශ්ය t
වූ rvalue සඳහනක්, අපි පැහැදිලි ලෙසම ප්රකාශ කිරීමට ඇති t
නිවැරදි ආපසු වර්ගය. Rvalue යොමු කිරීමක් ලබා දෙන ශ්රිතයක ඇමතුම xvalue වේ. Xvalues පැමිණෙන්නේ කොහෙන්දැයි දැන් ඔබ දන්නවා;)
Rvalue යොමු කිරීමක් ලබා දෙන ශ්රිතයක ඇමතුම std::move
xvalue වැනි ය.
t
ස්වයංක්රීය වස්තුවක් නිරූපණය නොකරන නමුත් ඒ වෙනුවට අමතන්නා විසින් සම්මත කරන ලද වස්තුවක් වන බැවින් මෙම උදාහරණයේ දී අගය යොමු කිරීම මඟින් ආපසු පැමිණීම හොඳ බව සලකන්න .