චලනය අර්ථ නිරූපණය යනු කුමක්ද?


1727

සී ++ 0x සම්බන්ධයෙන් ස්කොට් මේයර්ස් සමඟ මෘදුකාංග ඉංජිනේරු ගුවන් විදුලි පෝඩ්කාස්ට් සම්මුඛ සාකච්ඡාවට සවන් දීම මම අවසන් කළෙමි . නව විශේෂාංග බොහොමයක් මට තේරුමක් ඇති අතර, එකක් හැරුණු විට මම දැන් සී ++ 0x ගැන ඇත්තෙන්ම සතුටට පත්වෙමි. මට තවමත් චලනය අර්ථ නිරූපණයන් ලැබෙන්නේ නැත ... එය හරියටම කුමක්ද?


20
සී සහ සී ++ හි ඇති අගයන් සහ අගයන් පිළිබඳව [එලී බෙන්ඩර්ස්කිගේ බ්ලොග් ලිපිය] ( eli.thegreenplace.net/2011/12/15/… ) මට හමු විය . ඔහු C ++ 11 හි අගය යොමු කිරීම් ද සඳහන් කර කුඩා උදාහරණ සමඟ ඒවා හඳුන්වා දෙයි.
නිල්ස්


19
සෑම වසරකම හෝ ඊට වැඩි කාලයකදී මම කල්පනා කරන්නේ C ++ හි ඇති “නව” චලන අර්ථ නිරූපණය කුමක් ද යන්නයි, මම එය ගූගල් කර මෙම පිටුවට පිවිසෙන්න. මම ප්‍රතිචාර කියෙව්වා, මගේ මොළය වහලා. මම නැවත සී වෙත ගොස් සියල්ල අමතක කරමි! මම අවහිරයි.
අහස

8
@sky සලකා බලන්න std :: vector <> ... කොහේ හරි තැනක ගොඩේ අරාවකට දර්ශකයක් ඇත. ඔබ මෙම වස්තුව පිටපත් කරන්නේ නම් නව බෆරයක් වෙන් කළ යුතු අතර බෆරයේ දත්ත නව බෆරයට පිටපත් කළ යුතුය. හුදෙක් දර්ශකය සොරකම් කිරීම සුදුසු යැයි යම් තත්වයක් තිබේද? පිළිතුර ඔව්, සම්පාදකයා වස්තුව තාවකාලික බව දැනගත් විට. ඔබ ගමන් කරන වස්තුව ඉවතට යන බව සම්පාදකයා දන්නා විට ඔබේ පන්ති ධෛර්යය වෙනත් වස්තුවකට ඇද දැමිය හැකි ආකාරය නිර්වචනය කිරීමට චලනය අර්ථ නිරූපණය මඟින් ඉඩ ලබා දේ.
ඩයික්‍රෝස්

මට තේරුම් ගත හැකි එකම සඳහන: learncpp.com/cpp-tutorial/… , එනම් චලනය වන අර්ථකථනයේ මුල් හේතුව ස්මාර්ට් පොයින්ටර්ස් ය.
jw_

Answers:


2514

උදාහරණ කේතය සමඟ චලනය වන අර්ථ නිරූපණය තේරුම් ගැනීම පහසු බව මට පෙනේ. ගොඩ ගසන ලද මතක කොටසකට පමණක් දර්ශකයක් තබා ඇති ඉතා සරල නූල් පන්තියකින් පටන් ගනිමු:

#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"lvalues" වැනි ප්‍රකාශන හඳුන්වමු.

2 සහ 3 පේළි වල ඇති තර්ක lvalues ​​නොව ​​rvalues ​​වේ, මන්ද යත් යටින් පවතින නූල් වස්තූන් සඳහා නම් නොමැති බැවින් සේවාදායකයාට පසු කාලීනව ඒවා නැවත පරීක්ෂා කිරීමට ක්‍රමයක් නොමැත. rvalues ​​මඟින් ඊලඟ අර්ධ සළුවේදී විනාශ වන තාවකාලික වස්තූන් දක්වයි (වඩාත් නිවැරදිව කිවහොත්: පූර්ණ ප්‍රකාශනය අවසානයේ දී අක්ෂර වින්‍යාසය අඩංගු වේ). මෙය වැදගත් වන්නේ ආරම්භක අවධියේදී bසහ cඅපට අවශ්‍ය ඕනෑම දෙයක් ප්‍රභව නූලෙන් කළ හැකි අතර සේවාදායකයාට වෙනසක් පැවසිය නොහැක !

C ++ 0x විසින් "rvalue reference" නමින් නව යාන්ත්‍රණයක් හඳුන්වා දෙයි. අප කළ යුතුව ඇත්තේ rvalue යොමු පරාමිතියක් සහිත ඉදිකිරීම්කරුවෙකු ලිවීම පමණි. එම ඉදිකිරීම්කරු තුළ අපට යම්කිසි වලංගු තත්වයක පවතින තාක් කල් අපට අවශ්‍ය ඕනෑම දෙයක් ප්‍රභවය සමඟ කළ හැකිය :

    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ආරම්භ කරන්නේ කෙසේද? C ++ 98 හි පැරණි දිනවල , පිළිතුර "පිටපත් සාදන්නා විසින්" ලබා දෙනු ඇත. C ++ 0x හි, පැවරුම් ක්‍රියාකරුට වන තර්කය lvalue හෝ rvalue ද යන්න මත පදනම්ව සම්පාදකයා පිටපත් සාදන්නා සහ චලනය වන ඉදිකිරීම්කරු අතර තෝරා ගනී.

එබැවින් ඔබ කියන්නේ නම් a = b, පිටපත් සාදන්නා ආරම්භ කරනු ඇත that(ප්‍රකාශනය bඅගයක් බැවින්), සහ පැවරුම් ක්‍රියාකරු විසින් අලුතින් සාදන ලද ගැඹුරු පිටපතක් සමඟ අන්තර්ගතය මාරු කරයි. පිටපතෙහි අර්ථ දැක්වීම එයයි - අයිඩියම් මාරු කරන්න - පිටපතක් සාදන්න, පිටපත සමඟ අන්තර්ගතය මාරු කරන්න, ඉන්පසු විෂය පථයෙන් ඉවත්ව පිටපත ඉවත් කරන්න. මෙහි අලුත් දෙයක් නැත.

නමුත් ඔබ කියන්නේ නම් a = x + y, චලන ඉදිකිරීම්කරු ආරම්භ කරනු ඇත that(ප්‍රකාශනය x + yඅගයක් බැවින්), එබැවින් ගැඹුරු පිටපතක් සම්බන්ධ නොවේ, කාර්යක්ෂම පියවරක් පමණි. thatතවමත් තර්කයෙන් ස්වාධීන වස්තුවකි, නමුත් ගොඩවල් දත්ත පිටපත් කිරීමට අවශ්‍ය නොවූ හෙයින් එය තැනීම සුළුපටු ය. එය x + yඅගයක් බැවින් එය පිටපත් කිරීම අවශ්‍ය නොවීය, නැවතත්, අගයන් මඟින් දැක්වෙන නූල් වස්තූන්ගෙන් ගමන් කිරීම කමක් නැත.

සාරාංශගත කිරීම සඳහා, පිටපත් සාදන්නා ගැඹුරු පිටපතක් සාදයි, මන්ද ප්‍රභවය ස්පර්ශ නොවිය යුතුය. චලනය වන ඉදිකිරීම්කරුට, අනෙක් අතට, දර්ශකය පිටපත් කර ප්‍රභවයේ දර්ශකය ශුන්‍ය කිරීමට සැකසිය හැකිය. සේවාදායකයාට නැවත වස්තුව පරීක්ෂා කිරීමට ක්‍රමයක් නොමැති නිසා ප්‍රභව වස්තුව මේ ආකාරයෙන් “අහෝසි කිරීම” කමක් නැත.

මෙම උදාහරණය ප්‍රධාන කාරණය වටහා ගනු ඇතැයි මම බලාපොරොත්තු වෙමි. යොමු දැක්වීම් තක්සේරු කිරීමට සහ අර්ථකථන ගෙනයාමට තවත් බොහෝ දේ ඇත, එය සරලව තබා ගැනීම සඳහා මම හිතාමතාම අතහැර දැමුවෙමි. ඔබට වැඩි විස්තර අවශ්‍ය නම් කරුණාකර මගේ අතිරේක පිළිතුර බලන්න .


42
Ut නමුත් මගේ ctor ට අගයක් ලැබෙන්නේ නම්, එය කිසි විටෙකත් භාවිතා කළ නොහැකි නම්, එය ස්ථාවර / ආරක්ෂිත තත්වයක තැබීමට මට කරදර විය යුත්තේ ඇයි? That.data = 0 සැකසීම වෙනුවට, එය එසේ නොකරන්නේ මන්ද?
einpoklum

73
ineinpoklum නැතිනම් that.data = 0, චරිත ඉක්මනින් විනාශ වී යනු ඇත (තාවකාලික මිය ගිය විට), සහ දෙවරක්. ඔබට දත්ත සොරකම් කිරීමට අවශ්‍යයි, එය බෙදා නොගන්න!
fredoverflow

20
@einpoklum නිතිපතා උපලේඛනගත කර ඇති විනාශ කරන්නා තවමත් ක්‍රියාත්මක වන බැවින් ප්‍රභව වස්තුවෙහි පශ්චාත් චලනය වීමේ තත්වය බිඳ වැටීමක් ඇති නොවන බවට ඔබ සහතික විය යුතුය. වඩා හොඳ, ප්‍රභව වස්තුව පැවරුමක් හෝ වෙනත් ලිවීමක් ලබන්නා විය හැකි බවට ඔබ වග බලා ගත යුතුය.
CTMacUser

12
ranpranitkothari ඔව්, සියලු වස්තූන් විනාශ විය යුතුය, වස්තූන්ගෙන් පවා චලනය විය යුතුය. එය සිදු වූ විට වර්‍ග අරා මකා දැමීමට අපට අවශ්‍ය නැති නිසා, අපට දර්ශකය අහෝසි කළ යුතුය.
fredoverflow

9
Ull nullptr එකක delete[]ඇති Virus721, C ++ ප්‍රමිතිය මඟින් අර්ථ දැක්වීමක් නැත.
fredoverflow

1070

මගේ පළමු පිළිතුර අර්ථ නිරූපණය සඳහා අතිශය සරල හැඳින්වීමක් වූ අතර එය සරල ලෙස තබා ගැනීම සඳහා බොහෝ තොරතුරු අතහැර දමා ඇත. කෙසේ වෙතත්, අර්ථකථන ගෙනයාමට තවත් බොහෝ දේ ඇති අතර, හිඩැස් පිරවීම සඳහා දෙවන පිළිතුර සඳහා කාලය මෙය යැයි මම සිතුවෙමි. පළමු පිළිතුර දැනටමත් තරමක් පැරණි වන අතර එය සම්පූර්ණයෙන්ම වෙනස් පා with යකින් ආදේශ කිරීම නිවැරදි යැයි හැඟුනේ නැත. මම හිතන්නේ එය තවමත් පළමු හැඳින්වීමක් ලෙස සේවය කරයි. නමුත් ඔබට ගැඹුරට හෑරීමට අවශ්‍ය නම්, කියවන්න :)

වටිනා ප්‍රතිපෝෂණ ලබා දීමට ස්ටීවන් ටී. බොහොම ස්තූතියි, ස්ටීවන්!

හැදින්වීම

චලනය වන අර්ථ නිරූපණය මඟින් යම් වස්තුවකට, යම් යම් කොන්දේසි යටතේ වෙනත් වස්තුවක බාහිර සම්පත් වල අයිතිය ලබා ගැනීමට ඉඩ ලබා දේ. මෙය ආකාර දෙකකින් වැදගත් වේ:

  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, අතහැර දැමූ C ++ 98 සම්මත පුස්තකාල අච්චුවක් වන අතර එය std::unique_ptrC ++ 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_ptrauto_ptramake_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&&වෙනත් වර්ගයක සියලු වටිනාකම් කාණ්ඩ සමඟ බැඳී ඇත . එවැනි අවස්ථාවක, තාවකාලික වර්ගයක් නිර්මාණය වන අතර, අගය යොමු කිරීම එම තාවකාලිකයට බැඳී ඇත:YYXX

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::movexvalue වැනි ය.

tස්වයංක්‍රීය වස්තුවක් නිරූපණය නොකරන නමුත් ඒ වෙනුවට අමතන්නා විසින් සම්මත කරන ලද වස්තුවක් වන බැවින් මෙම උදාහරණයේ දී අගය යොමු කිරීම මඟින් ආපසු පැමිණීම හොඳ බව සලකන්න .



24
චලනය වීමේ අර්ථ නිරූපණය වැදගත් වන තුන්වන හේතුවක් තිබේ: ව්‍යතිරේක ආරක්ෂාව. බොහෝ විට පිටපත් මෙහෙයුමක් විසි කළ හැකි තැනක් (එයට සම්පත් වෙන් කිරීම අවශ්‍ය වන අතර ප්‍රතිපාදන අසමත් විය හැකිය) චලනය කිරීමේ මෙහෙයුමක් ඉවත දැමිය නොහැක (එයට නව ඒවා වෙන් කිරීම වෙනුවට පවතින සම්පත් වල අයිතිය පැවරිය හැකි බැවිනි). අසමත් විය නොහැකි මෙහෙයුම් තිබීම සැමවිටම හොඳයි, ව්‍යතිරේක සහතික සපයන කේත ලිවීමේදී එය තීරණාත්මක විය හැකිය.
බ්‍රැන්ග්ඩන්

8
'විශ්වීය යොමු කිරීම්' දක්වා මම ඔබ සමඟ සිටියෙමි, නමුත් එය අනුගමනය කිරීමට නොහැකි තරම් වියුක්තය. යොමු බිඳ වැටීම? පරිපූර්ණ ඉදිරියට යැවීම? වර්ගය සැකසූ විට අගය යොමු කිරීමක් විශ්වීය යොමු කිරීමක් බවට ඔබ පවසනවාද? මෙය පැහැදිලි කිරීමට ක්‍රමයක් තිබුනේ නම් මම එය තේරුම් ගත යුතුද නැද්ද යන්න දැන ගනු ඇත! :)
Kylotan

8
කරුණාකර දැන් පොතක් ලියන්න ... මෙම පිළිතුර මට විශ්වාස කිරීමට හේතුවක් ලබා දී ඇත්තේ ඔබ C ++ හි වෙනත් කොන මේ ආකාරයෙන් පැහැදිලි ආකාරයකින් ආවරණය කළහොත් තවත් දහස් ගණනක් දෙනා එය තේරුම් ගනු ඇත.
හැලිවින්ස්ටන්

12
ඔබගේ කාරුණික ප්‍රතිපෝෂණයට බොහෝම ස්තූතියි, මම එය ඇත්තෙන්ම අගය කරමි. පොතක් ලිවීමේ ගැටලුව නම්: එය ඔබට සිතාගත නොහැකි තරම් වැඩකි. ඔබට C ++ 11 සහ ඉන් ඔබ්බට ගැඹුරට හාරා බැලීමට අවශ්‍ය නම්, ස්කොට් මේයර්ස් විසින් "Effective ලදායී නවීන C ++" මිලදී ගැනීමට මම යෝජනා කරමි.
fredoverflow

78

චලන අර්ථකථන පදනම් වී ඇත්තේ අගය යොමු කිරීම් මත ය .
Rvalue යනු තාවකාලික වස්තුවක් වන අතර එය ප්‍රකාශනය අවසානයේ විනාශ වීමට නියමිතය. වත්මන් C ++ හි, අගයන් බැඳී ඇත්තේ 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));නම් කරන ලද rvalue යොමු කිරීම් lvalues ​​වන බැවිනි
wmamrak

මෙම @ Tacyt ගේ අදහස් අනුව, ටිකක් වැරදි: rhsයනු lvalue සන්දර්භය තුළ X::X(X&& rhs). std::move(rhs)අගයක් ලබා ගැනීම සඳහා ඔබ ඇමතිය යුතුය, නමුත් මෙය කාරුණික පිළිතුරක් ලබා දෙයි.
අෂෙරා

දර්ශකයන් නොමැතිව වර්ග සඳහා අර්ථ නිරූපණය කරන්නේ කුමක්ද? අර්ථකථන ගෙනයාම සමාන පිටපතක් ගෙනයන්න?
ගුසෙව් ස්ලාවා

Us ගුසෙව්: ඔබ ඉල්ලන්නේ කුමක්දැයි මට අදහසක් නැත.
sbi

60

ඔබට සැලකිය යුතු වස්තුවක් ලබා දෙන ශ්‍රිතයක් ඇතැයි සිතමු:

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

ඔබ මේ ආකාරයට කේත ලියන විට:

Matrix r = multiply(a, b);

එවිට සාමාන්‍ය C ++ සම්පාදකයෙකු එහි ප්‍රති result ලය සඳහා තාවකාලික වස්තුවක් නිර්මාණය කරනු ඇත, ආරම්භ කිරීම සඳහා multiply()පිටපත් සාදන්නා අමතන්න r, පසුව තාවකාලික ප්‍රතිලාභ අගය විනාශ කරයි. C ++ 0x හි චලනය වන අර්ථකථන rඑහි අන්තර්ගතය පිටපත් කිරීමෙන් "චලනය වන ඉදිකිරීම්කරු" ආරම්භයට කැඳවීමට ඉඩ දෙයි , පසුව එය විනාශ නොකර තාවකාලික අගය ඉවතලන්න.

මෙය විශේෂයෙන් වැදගත් වන්නේ (සමහර විට Matrixඉහත උදාහරණය මෙන් ), පිටපත් කරන ලද වස්තුව එහි අභ්‍යන්තර නිරූපණය ගබඩා කිරීම සඳහා ගොඩවල් මත අමතර මතකයක් වෙන් කරයි නම්. පිටපත් සාදන්නෙකුට අභ්‍යන්තර නිරූපණයේ සම්පූර්ණ පිටපතක් සෑදිය යුතුය, නැතහොත් විමර්ශන ගණන් කිරීම සහ පිටපත් මත ලිවීමේ අර්ථ නිරූපණයන් භාවිතා කළ යුතුය. චලනය වන ඉදිකිරීම්කරුවෙකු ගොඩවල් මතකය තනිවම තබා Matrixවස්තුව තුළ දර්ශකය පිටපත් කරයි .


2
චලන ඉදිකිරීම්කරුවන් සහ පිටපත් සාදන්නන් වෙනස් වන්නේ කෙසේද?
ඩයික්‍රෝස්

1
ic ඩයික්‍රෝස්: ඒවා වාක්‍ය ඛණ්ඩයෙන් වෙනස් වේ, එකක් මැට්‍රික්ස් (කොන්ස්ට් මැට්‍රික්ස් සහ එස්ආර්සී) (පිටපත් සාදන්නා) මෙන් පෙනේ, අනෙක මැට්‍රික්ස් (මැට්‍රික්ස් සහ ඇන්ඩ් එස්ආර්සී) (චලනය වන ඉදිකිරීම්කරු) මෙන් පෙනේ, වඩා හොඳ උදාහරණයක් සඳහා මගේ ප්‍රධාන පිළිතුර පරීක්ෂා කරන්න.
snk_kid

3
ic ඩයික්‍රෝස්: යමෙක් හිස් වස්තුවක් සාදයි, තවත් එකක් පිටපතක් සාදයි. වස්තුව තුළ ගබඩා කර ඇති දත්ත විශාල නම්, පිටපතක් මිල අධික විය හැකිය. උදාහරණයක් ලෙස, std :: vector.
බිලී ඔනේල්

1
un kunj2aan: එය ඔබේ සම්පාදකයා මත රඳා පවතී. සම්පාදකයාට ශ්‍රිතය තුළ තාවකාලික වස්තුවක් නිර්මාණය කළ හැකි අතර පසුව එය අමතන්නාගේ ප්‍රතිලාභ අගයට ගෙන යා හැකිය. නැතහොත්, චලනය වන ඉදිකිරීම්කරුවෙකු භාවිතා කිරීමකින් තොරව, ප්‍රතිලාභ අගයෙහි වස්තුව කෙලින්ම තැනීමට එයට හැකි විය හැකිය.
ග්‍රෙග් හෙව්ගිල්

2
Ic ජිචාවෝ: එය RVO නමින් හැඳින්වෙන ප්‍රශස්තිකරණයකි, වෙනස පිළිබඳ වැඩි විස්තර සඳහා මෙම ප්‍රශ්නය බලන්න: stackoverflow.com/questions/5031778/…
ග්‍රෙග් හෙව්ගිල්

30

චලන අර්ථ නිරූපණය පිළිබඳ හොඳ, ගැඹුරු පැහැදිලි කිරීමක් ගැන ඔබ සැබවින්ම උනන්දුවක් දක්වන්නේ නම්, “සී ++ භාෂාවට චලනය වන අර්ථකථන සහය එක් කිරීමට යෝජනාවක්” යන මුල් පත්‍රය කියවීමට මම නිර්දේශ කරමි .

එය ඉතා පහසුවෙන් හා පහසුවෙන් කියවිය හැකි අතර එය ඔවුන් ලබා දෙන ප්‍රතිලාභ සඳහා විශිෂ්ට අවස්ථාවක් සපයයි. ලබා ගත හැකි මත පියවර semantics ගැන අනෙක් වඩාත් මෑත සහ දිනය දක්වා පත්ර ඇත මෙම WG21 වෙබ් අඩවිය , නමුත්, එය ඉහළ මට්ටමේ දැක්ම දේවල් ළඟා සහ භාවිතයෙන් භාෂාව විස්තර ඇතුළු ඉතා ලබා නැත සිට මේ එක සමහර විට වඩාත් පහසු වේ.


28

චලනය අර්ථ නිරූපණය යනු කිසිවෙකුට තවදුරටත් ප්‍රභව වටිනාකම අවශ්‍ය නොවන විට ඒවා පිටපත් කරනවාට වඩා සම්පත් මාරු කිරීමයි .

C ++ 03 හි, වස්තූන් බොහෝ විට පිටපත් කරනු ලැබේ, ඕනෑම කේතයක් නැවත අගය භාවිතා කිරීමට පෙර විනාශ කිරීමට හෝ පැවරීමට පමණි. නිදසුනක් ලෙස, ඔබ ශ්‍රිතයකින් අගය අනුව ආපසු එන විට R RVO ආරම්භ නොවන්නේ නම් you ඔබ ආපසු එන අගය අමතන්නාගේ සිරස් රාමුවට පිටපත් කරනු ලැබේ, පසුව එය විෂය පථයෙන් බැහැර වී විනාශ වේ. මෙය බොහෝ උදාහරණ වලින් එකකි: ප්‍රභව වස්තුව තාවකාලික වූ විට සම්මතයෙන් අගය බලන්න, ඇල්ගොරිතම වැනි sortඅයිතම නැවත සකස් කිරීම, එය ඉක්මවා ගිය vectorවිට නැවත ස්ථානගත කිරීම capacity()යනාදිය බලන්න.

එවැනි පිටපත් / විනාශ කිරීමේ යුගල මිල අධික වූ විට, එය සාමාන්‍යයෙන් වස්තුවට යම් හෙවිවේට් සම්පතක් ඇති බැවිනි. නිදසුනක් ලෙස, වස්තු vector<string>සමූහයක් අඩංගු ගතිකව වෙන් කරන ලද මතක බ්ලොක් එකක් තිබිය stringහැකිය, සෑම එකක්ම එහි ගතික මතකය ඇත. එවැනි වස්තුවක් පිටපත් කිරීම මිල අධිකය: ප්‍රභවයේ ගතිකව වෙන් කර ඇති එක් එක් කොටස් සඳහා ඔබට නව මතකයක් වෙන් කළ යුතු අතර, සියලු අගයන් පිටපත් කරන්න. එවිට ඔබ පිටපත් කළ එම මතකය අවලංගු කිරීමට ඔබට අවශ්‍යය. කෙසේ වෙතත්, ගමන් විශාල vector<string>මාර්ගයෙන් පමණක් ගමනාන්තය දක්වා (ගතික මතක වාරණ වෙත යොමු වන්න බව) කිහිපයක් සූචක පිටපත් සහ මුලාශ්රය ඔවුන් පිටතට zeroing.


24

පහසු (ප්‍රායෝගික) වචන වලින්:

වස්තුවක් පිටපත් කිරීම යනු එහි “ස්ථිතික” සාමාජිකයන් පිටපත් කිරීම සහ 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. 
};

හරි, නමුත් මම වස්තුවක් ගෙන ගියහොත්, ප්‍රභව වස්තුව නිෂ් less ල වේ, නැත? ඇත්ත වශයෙන්ම, නමුත් සමහර අවස්ථාවන්හිදී එය ඉතා ප්රයෝජනවත් වේ. වඩාත්ම පැහැදිලිව පෙනෙන දෙය නම් මම නිර්නාමික වස්තුවක් සහිත ශ්‍රිතයක් අමතන විට (තාවකාලික, අගනා වස්තුව, ..., ඔබට එය විවිධ නම් වලින් ඇමතිය හැකිය):

void heavyFunction(HeavyType());

එම අවස්ථාවෙහිදී, නිර්නාමික වස්තුවක් සාදනු ලැබේ, ඊළඟට ශ්‍රිත පරාමිතියට පිටපත් කර පසුව මකා දමනු ලැබේ. ඉතින්, මෙහිදී ඔබට වස්තුව චලනය කිරීම වඩා හොඳය, මන්ද ඔබට නිර්නාමික වස්තුව අවශ්‍ය නොවන අතර ඔබට කාලය හා මතකය ඉතිරි කර ගත හැකිය.

මෙය "rvalue" යොමුවක සංකල්පයට මග පාදයි. ඒවා C ++ 11 හි පවතින්නේ ලැබුණු වස්තුව නිර්නාමිකද නැද්ද යන්න හඳුනා ගැනීමට පමණි. "Lvalue" යනු පැවරිය හැකි වස්තුවක් ( =ක්‍රියාකරුගේ වම් කොටස ) බව ඔබ දැනටමත් දන්නා බව මම සිතමි , එබැවින් ඔබට අගයක් ලෙස ක්‍රියා කිරීමට හැකියාව ඇති වස්තුවකට නම් කළ යොමු කිරීමක් අවශ්‍ය වේ. Rvalue යනු හරියටම ප්‍රතිවිරුද්ධ දෙයකි. එම හේතුව නිසා, නිර්නාමික වස්තුව සහ අගය යනු සමාන පද වේ. නිසා:

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"පිටපත් කළ යුතු" විට, සම්පාදකයා විසින් සම්මත කරන ලද වස්තුව නම් කර තිබේද නැද්ද යන්න අනුව lvalue යොමු කිරීමක් හෝ අගය යොමු කිරීමක් නිර්මාණය කරයි. එසේ නොවන විට, ඔබේ චලනය-ඉදිකිරීම්කරු කැඳවනු ලබන අතර, එම වස්තුව තාවකාලික බව ඔබ දන්නා අතර, ඒවායේ ගතික වස්තූන් පිටපත් කරනවා වෙනුවට චලනය කළ හැකිය, අවකාශය සහ මතකය ඉතිරි වේ.

"ස්ථිතික" වස්තු සෑම විටම පිටපත් කර ඇති බව මතක තබා ගැනීම වැදගත්ය. ස්ථිතික වස්තුවක් "චලනය" කිරීමට ක්‍රම නොමැත (වස්තුව සිරස්ව ඇති අතර ගොඩවල් මත නොවේ). එබැවින්, වස්තුවකට ගතික සාමාජිකයන් නොමැති විට (සෘජුව හෝ වක්‍රව) “චලනය” / “පිටපත” යන වෙනස අදාළ නොවේ.

ඔබේ වස්තුව සංකීර්ණ නම් සහ විනාශ කරන්නා වෙනත් ද්විතියික බලපෑම් ඇති කරයි නම්, පුස්තකාලයක ශ්‍රිතයකට ඇමතීම, වෙනත් ගෝලීය කාර්යයන් සඳහා ඇමතීම හෝ එය කුමක් වුවත්, ධජයක් සමඟ චලනය සං signal ා කිරීම වඩා හොඳය:

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යනු rvalue හා 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 = A &), ( A & && => A &) Tසඳහා අගයන් යොමු කිරීමක් නම් . නම් කිරීමට rvalue සමුද්දේශ , ද (ඒ && && => ඒ &&.) මෙම අවස්ථා දෙකෙහිම, නියම විෂය පථයේ නම් කරන ලද වස්තුවකි, නමුත් ඇමතුම් විෂය පථයේ දෘෂ්ටි කෝණයෙන් එහි “යොමු වර්ගය” පිළිබඳ තොරතුරු අඩංගු වේ. මෙම තොරතුරු ( ) අච්චු පරාමිතිය ලෙස සම්මත කර ඇති අතර 'a' ගෙන යනු ලැබේ හෝ වර්ගය අනුව නොවේ .AaTAaaTTforwardT


20

එය පිටපත් අර්ථ නිරූපණයන් වැනි ය, නමුත් "ගෙන යන" වස්තුවෙන් දත්ත සොරකම් කිරීමට ඔබට ලැබෙන සියලු දත්ත අනුපිටපත් කිරීම වෙනුවට.


13

පිටපත් අර්ථ නිරූපණයක තේරුම කුමක්දැයි ඔබ දන්නවාද? එහි අර්ථය වන්නේ ඔබට පිටපත් කළ හැකි වර්ග ඇති බවයි, පරිශීලක-නිර්වචනය කළ වර්ග සඳහා ඔබ මෙය අර්ථ දක්වන්නේ එක්කෝ පිටපත් සාදන්නෙකු සහ පැවරුම් ක්‍රියාකරුවෙකු පැහැදිලිව ලිවීම හෝ සම්පාදකයා ඒවා ව්‍යංගයෙන් ජනනය කරයි. මෙය පිටපතක් කරනු ඇත.

චලනය අර්ථ නිරූපණය යනු මූලික වශයෙන් පරිශීලක-නිර්වචනය කරන ලද වර්ගයකි, එය r- අගය යොමු කිරීමක් (&& (ඔව් ඇම්පර්සෑන්ඩ් දෙකක් භාවිතා කරමින්) භාවිතා කරයි, එය නියත නොවන, මෙය චලන ඉදිකිරීම්කරුවෙකු ලෙස හැඳින්වේ, පැවරුම් ක්‍රියාකරු සඳහාද වේ. එනිසා චලනය වන ඉදිකිරීම්කරුවෙකු කරන්නේ කුමක්ද, එහි මූලාශ්‍ර තර්කයෙන් මතකය පිටපත් කරනවා වෙනුවට එය ප්‍රභවයේ සිට ගමනාන්තය දක්වා මතකය 'චලනය' කරයි.

ඔබට එය කිරීමට අවශ්‍ය වන්නේ කවදාද? well std :: දෛශිකය නිදසුනකි, ඔබ තාවකාලික std :: දෛශිකයක් නිර්මාණය කළ බව පවසමින් ඔබ එය ශ්‍රිතයකින් ආපසු ලබා දෙන්න:

std::vector<foo> get_foos();

ශ්‍රිතය නැවත පැමිණෙන විට (සහ එය C ++ 0x වලින් වනු ඇත) std :: දෛශිකයට පිටපත් කිරීම වෙනුවට චලනය වන ඉදිකිරීම්කරුවෙකු සිටී නම් එය පිටපත් කිරීම වෙනුවට එහි දර්ශක සකසා ගතිකව වෙන් කළ හැකිය. මතකය නව අවස්ථාව වෙත. එය std :: auto_ptr සමඟ හිමිකාරිත්වය මාරු කිරීම වැනි ය.


1
මෙය හොඳ උදාහරණයක් යැයි මම නොසිතමි, මන්ද මෙම ශ්‍රිත ප්‍රතිලාභ අගය උදාහරණ වලදී ප්‍රතිලාභ අගය ප්‍රශස්තිකරණය දැනටමත් පිටපත් ක්‍රියාකාරිත්වය ඉවත් කර ඇත.
සැන් ලින්ක්ස්

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එක් වැදගත් වෙනසක්, සමුද්දේශ:
rvalue සඳහනක් නොකළ යුතුයි සහ lvalue කළ නොහැකි ය.

Cppreference.com වෙතින් :

ප්‍රබල ව්‍යතිරේක සහතිකයක් ලබා දීම සඳහා, පරිශීලක-නිර්වචනය කළ චලන ඉදිකිරීම්කරුවන් ව්‍යතිරේකයන් විසි නොකළ යුතුය. ඇත්ත වශයෙන්ම, බහාලුම් මූලද්‍රව්‍ය නැවත ස්ථානගත කිරීමට අවශ්‍ය වූ විට චලනය හා පිටපත් අතර තෝරා ගැනීම සඳහා සම්මත බහාලුම් සාමාන්‍යයෙන් std :: move_if_noexcept මත රඳා පවතී. පිටපත් සහ චලන ඉදිකිරීම්කරුවන් දෙකම සපයා තිබේ නම්, තර්කය යනු අගයක් නම්, අධි බර විභේදනය මඟින් චලන ඉදිකිරීම්කරු තෝරා ගනී (එක්කෝ නම් රහිත තාවකාලික වැනි අගයක් හෝ std :: move හි ප්‍රති as ලයක් වැනි xvalue), සහ පිටපත් සාදන්නා තෝරා ගන්නේ නම් තර්කය යනු අගයක් වේ (නම් කරන ලද වස්තුවක් හෝ ශ්‍රිතයක් / ක්‍රියාකරුවෙකු ආපසු පැමිණීමේ අගය යොමු කිරීම). පිටපත් සාදන්නා පමණක් සපයා තිබේ නම්, සියලු තර්ක කාණ්ඩ එය තෝරා ගනී (එය නියත යොමු කිරීමක් ගන්නා තාක් කල්, අගයන් නියත යොමු කිරීම් සමඟ බැඳිය හැකි බැවින්), එමඟින් චලනය සඳහා ඇති පසුබෑම පිටපත් කිරීම සිදු කරයි. බොහෝ අවස්ථාවන්හීදී, චලනය වන ඉදිකිරීම්කරුවන් නිරීක්ෂණය කළ හැකි අතුරු ආබාධ ඇති කළත් ඒවා ප්‍රශස්ත කර ඇත, පිටපත් ඉවත් කිරීම බලන්න. පරාමිතියක් ලෙස අගය යොමු කිරීමක් ගන්නා විට ඉදිකිරීම්කරුවෙකු 'චලනය වන ඉදිකිරීම්කරු' ලෙස හැඳින්වේ. කිසිවක් ගෙනයාමට බැඳී නොසිටින අතර, පන්තියට ගෙනයාමට සම්පතක් අවශ්‍ය නොවන අතර පරාමිතිය a හි ඉඩදිය හැකි (නමුත් සමහර විට සංවේදී නොවේ) නඩුවේදී මෙන් 'චලනය වන ඉදිකිරීම්කරුවෙකුට' සම්පතක් ගෙනයාමට නොහැකි විය හැකිය. const rvalue යොමුව (const T &&).


7

මම මෙය ලියන්නේ එය නිසි ලෙස තේරුම් ගැනීමට වග බලා ගැනීම සඳහා ය.

විශාල වස්තූන් අනවශ්‍ය ලෙස පිටපත් කිරීම වළක්වා ගැනීම සඳහා චලන අර්ථකථන නිර්මාණය කරන ලදි. බජාර්න් ස්ට්‍රෝස්ට්‍රප් සිය "සී ++ ක්‍රමලේඛන භාෂාව" නම් ග්‍රන්ථයේ අනවශ්‍ය පිටපත් කිරීම පෙරනිමියෙන් සිදු වන උදාහරණ දෙකක් භාවිතා කරයි: එකක්, විශාල වස්තූන් දෙකක් මාරු කිරීම සහ දෙකක්, ක්‍රමයකින් විශාල වස්තුවක් ආපසු පැමිණීම.

විශාල වස්තූන් දෙකක් මාරු කිරීම සාමාන්‍යයෙන් පළමු වස්තුව තාවකාලික වස්තුවකට පිටපත් කිරීම, දෙවන වස්තුව පළමු වස්තුවට පිටපත් කිරීම සහ තාවකාලික වස්තුව දෙවන වස්තුවට පිටපත් කිරීම යනාදිය ඇතුළත් වේ. සාදන ලද වර්ගයක් සඳහා, මෙය ඉතා වේගවත් ය, නමුත් විශාල වස්තූන් සඳහා මෙම පිටපත් තුන සඳහා විශාල කාලයක් ගතවනු ඇත. "චලනය කිරීමේ පැවරුමක්" මඟින් ක්‍රමලේඛකයාට පෙරනිමි පිටපත් හැසිරීම අභිබවා යාමට ඉඩ සලසයි, ඒ වෙනුවට වස්තූන් වෙත යොමු කිරීම් මාරු කරයි, එයින් අදහස් කරන්නේ කිසිසේත්ම පිටපත් කිරීමක් නොමැති අතර හුවමාරු ක්‍රියාකාරිත්වය වඩා වේගවත් බවය. Std :: move () ක්‍රමය ඇමතීමෙන් චලනය පැවරුම ක්‍රියාත්මක කළ හැකිය.

ක්‍රමයකින් වස්තුවක් පෙරනිමියෙන් ආපසු යැවීම යනු දේශීය වස්තුවේ පිටපතක් සහ ඒ හා සම්බන්ධ දත්ත ඇමතුම්කරුට ප්‍රවේශ විය හැකි ස්ථානයක සෑදීමයි (මන්ද දේශීය වස්තුව ඇමතුම්කරුට ප්‍රවේශ විය නොහැකි අතර ක්‍රමය අවසන් වූ විට අතුරුදහන් වේ). සාදන ලද වර්ගයක් ආපසු ලබා දෙන විට, මෙම ක්‍රියාව ඉතා වේගවත් ය, නමුත් විශාල වස්තුවක් ආපසු ලබා දෙන්නේ නම්, මෙය බොහෝ කාලයක් ගතවනු ඇත. චලනය වන ඉදිකිරීම්කරු ක්‍රමලේඛකයාට මෙම පෙරනිමි හැසිරීම අභිබවා යාමට ඉඩ සලසයි. ඒ වෙනුවට දේශීය වස්තුව හා සම්බන්ධ දත්ත ගොඩගැසීමට වස්තුව නැවත අමතන්නා වෙත යොමු කිරීමෙන් දේශීය වස්තුව හා සම්බන්ධ ගොඩවල් දත්ත නැවත භාවිතා කරයි. මේ අනුව පිටපත් කිරීමක් අවශ්‍ය නොවේ.

දේශීය වස්තූන් නිර්මාණය කිරීමට ඉඩ නොදෙන භාෂාවල (එනම්, තොගයේ ඇති වස්තූන්) මෙම ආකාරයේ ගැටළු ඇති නොවන්නේ සියලු වස්තූන් ගොඩවල් මත වෙන් කර ඇති අතර ඒවා සැමවිටම යොමු කිරීමෙනි.


"A" චලන පැවරුම "මඟින් ක්‍රමලේඛකයාට පෙරනිමි පිටපත් හැසිරීම අභිබවා යාමට ඉඩ සලසයි. - මෙම ප්‍රකාශ අපැහැදිලි සහ නොමඟ යවන සුළුය. වස්තූන් දෙකක් හුවමාරු කර ගැනීමට xසහ yඔබට “වස්තූන් වෙත යොමු කිරීම් මාරු කළ නොහැක ; වස්තූන් වෙනත් දත්ත යොමු කරන දර්ශක අඩංගු විය හැකි අතර, එම දර්ශකයන් මාරු කළ හැකිය, නමුත් චලනය වන ක්‍රියාකරුවන්ට කිසිවක් මාරු කිරීමට අවශ්‍ය නොවේ . ඔවුන් එහි ඇති ඩෙස්ට් දත්ත ආරක්ෂා කරනවාට වඩා, චලනය කළ වස්තුවෙන් දත්ත අතුගා දැමිය හැකිය.
ටෝනි ඩෙල්රෝයි

swap()චලනය අර්ථ නිරූපණයකින් තොරව ඔබට ලිවිය හැකිය . "Std :: move () ක්‍රමය ඇමතීමෙන් චලනය පැවරුම ක්‍රියාත්මක කළ හැකිය." - සමහර විට එය භාවිතා කිරීම අවශ්‍ය වේ std::move()- එය ඇත්ත වශයෙන්ම කිසිවක් චලනය නොකලද - තර්කය චලනය කළ හැකි බව සම්පාදකයාට දැන ගැනීමට ඉඩ දෙයි, සමහර විට std::forward<>()(යොමු යොමු කිරීම් සහිතව), සහ වෙනත් වේලාවක සම්පාදකයාට වටිනාකමක් ගෙන යා හැකි බව දනී.
ටෝනි ඩෙල්රෝයි

-2

මෙන්න බර්න් ස්ට්‍රෝස්ට්‍රප් විසින් රචිත "සී ++ ක්‍රමලේඛන භාෂාව" පොතෙන් පිළිතුරක් . ඔබට වීඩියෝව බැලීමට අවශ්‍ය නැතිනම්, ඔබට පහත පෙළ දැකිය හැකිය:

මෙම ස්නිපටය සලකා බලන්න. ක්‍රියාකරුවෙකුගෙන් ආපසු පැමිණීම යනු දේශීය විචල්‍යයෙන් ප්‍රති the ලය පිටපත් කිරීම 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;
}

අපට ඇත්තටම පිටපතක් අවශ්‍ය නොවීය; අපට අවශ්‍ය වූයේ ශ්‍රිතයක ප්‍රති result ලය ලබා ගැනීමටයි. එබැවින් අපට එය පිටපත් කිරීමට වඩා දෛශිකයක් ගෙන යා යුතුය. අපට චලන ඉදිකිරීම්කරු පහත පරිදි අර්ථ දැක්විය හැකිය:

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

&& යන්නෙහි තේරුම “rvalue reference” වන අතර එය අපට අගයක් බැඳ තැබිය හැකි යොමු කිරීමකි. "rvalue" යන්නෙහි අර්ථය "lvalue" යන්නට අනුපූරක වන අතර එහි දළ වශයෙන් අදහස් වන්නේ "පැවරුමක වම් පසින් දිස්විය හැකි දෙයක්" යන්නයි. එබැවින් අගය යනු දළ වශයෙන් "ඔබට පැවරිය නොහැකි අගයක්", එනම් ශ්‍රිත ඇමතුමකින් ආපසු ලබා දෙන පූර්ණ සංඛ්‍යාවක් සහ resදෛශික සඳහා ක්‍රියාකරු + () හි දේශීය විචල්‍යයයි.

දැන්, ප්රකාශය return res;පිටපත් නොකරනු ඇත!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.