rvalue چیست؟
بر اساس تعریف تو C++11 به عبارتی lvalue گفته میشه که تو حافظه جایی داره و با عملگر & میشه بهش دسترسی داشت. به هر عبارتی که lvalue نباشه rvalue میگن. lvalueها میتونن هم طرف راست و هم طرف چپ یه عبارت انتساب قرار بگیرن، ولی rvalueها فقط میتونن طرف راست باشن.
مثلا اگر a و b و c هر سه از نوع int باشن، عبارتهای a و b و c از نوع lvalue و عبارت a+b و c-2 و ۲۴ از نوع rvalue هستن. طبیعتا یه فانکشنی که داره از نوع مثلا double برمیگردونه، مقدارش از نوع rvalueه (تا حال دیدین بشه نوشت sin(x)=2;؟)
به چه درد میخوره؟
فرض کنید دارید یه کلاس ماتریس مینویسید. در حالت عادی عملگر انتساب شما ماتریس سمت راست رو میگیری و به کپی از دادهها تو ماتریس سمت چپی ایجاد میکنه. حالا برای این کلاسمون به عملگر جمع عم مینویسم که این عملگر جمع خودش یه ماتریس حاصل برمیگردونه. اگه عملگر مساوی مث سابق عمل بکنه، باید دادههای حاصل رو سمت چپ تساوی کپی بکنه. تو این حالت ماتریس حاصل جمع اضافه میمونه. یه راه بهتر اینه که فقط پویینتر دادههای ماتریس حاصل جمع کپی بشه. البته این راه حل وقتی خوبه که طرف راست تساوی از نوع rvalue باشه. این جاست که rvalue ها مهم میشن. با داشتن نوع rvalue میتونیم یه فانکشن رو سربارگزاری کنیم و بگیم اگه پارامترت rvalue بود یه نوع خاص رفتار بکن. سینتکسش هم که خیلی سادست:
[cpp]
void foo(X& x); // lvalue reference overload
void foo(X&& x); // rvalue reference overload
X x;
X foobar();
foo(x); // argument is lvalue: calls foo(X&)
foo(foobar()); // argument is rvalue: calls foo(X&&)
[/cpp]
بهترین کاربرد این نوع از فانکشنها برای سازندهها و عملگرهای انتسابه. تو اینجور عملگرهای انتساب، باید آبجکت سمت چپ نابود بشه و رفرنسهای آبجکت سمت راست به آبجکت سمت چپ منتقل بشن (و نه کپی خونه به خونه). باید توجه کرد تو خود فانکشنی که پارامتر رو گرفته، اون پارامتر به صورت lvalue در نظر گرفته میشه.
البته تاکید کنم که از قدیم میتونستیم فانکشنمون رو به صورت
[cpp]
void foo(X const &x);
[/cpp]
تعریف کنیم که هم رو lvalueها کار میکنه هم رو rvalueها، اما فرقی بینشون نمیذاره.
فانکشن stl::move
این فانکشن یه lvalue رو میگیره و به صورت rvlue برمیگردونه. با استفاده از این فانکشن، میشه برنامه رو مجبور کرد که با یه متغیر به صورت rvalue کار کنه که به موقعش میتونه خیلی کمک کننده باشه.
یه کاربرد مهم این فانکشن تو سازندههای کلاسهای مشتق شدست. مثلا این سازنده رو
[cpp]
Derived(Derived&& rhs)
: Base(rhs) // wrong: rhs is an lvalue
{
// Derived-specific stuff
}
[/cpp]
رو با این یکی
[cpp]
Derived(Derived&& rhs)
: Base(std::move(rhs)) // good, calls Base(Base&& rhs)
{
// Derived-specific stuff
}
[/cpp]
مقایسه کنید. مشخصا سازنده اول غلط عمل میکنه.
فراخوانی فانکشنها
یه استفاده مهم دیگه از پارامترهای به شکل rvalue برای جلوگیری از کپی شدن متغیرها موقع پاس دادن اونهاست. فرض کنید یه فانکشن مثل این داریم:
[cpp]
template
shared_ptr factory(Arg arg)
{
return shared_ptr(new T(arg));
}
[/cpp]
بدیش اینجاست که این فانکشن یه کپی اضافه از arg موقع پاس دادن به سازنده T میسازه و ما میخوایم جلوی این رو بگیریم. با استفاده از فانکشن std::forward (که هرکدوم از lvalue و rvalue رو به اون یکی تبدیل میکنه) و این سینتکس مشکل حل میشه:
[cpp]
template
shared_ptr factory(Arg&& arg)
{
return shared_ptr(new T(std::forward(arg)));
}
[/cpp]
این نوشته خلاصهای از منبع اصلی است.