بهتره از کد اول که وکتور return میشه استفاده کرد چون خواناتر هست و اکثر اوقات هیچ سربار خاصی نداره و معادل فرستادن با refrence هست .
دلیل این که این 2 مورد اکثر اوقات با هم تفاوتی ندارن برمیگرده به بهینه سازی های مربوط به کامپایلر ( RVO , NRVO )
زمانی که یک مقدار از تابع برگشت داده میشه 2 تا کپی گرفته میشه :
1_کپی مقدار در حال بازگشت به یک فضای موقت
2_ کپی شدن مقدار از فضای موقت به جایی که از اون جا تابع صدا زده شده .
int foo()
{
return 1; //inja 1 copy anjam mishe
}
int c=foo();//inja copy dovom
کامپایلر ها 2 روش بهینه سازی مربوط به مقدار برگشتی از توابع دارن RVO , NRVO .
RVO مربوط میشه به حذف مورد اول که خیلی وقته کامپایلر ها این مورد رو پشتیبانی می کنن و تقریبا همه جا این کپی اضافی حذف میشه .
NRVO مربوط میشه به حذف کپی دوم
برای این که NRVO و RVO انجام بشه موارد زیر باید رعایت بشه :
1_تابع باید از یک جا return بشه
int foo()//NRVO fail
{
if(1)
return 1;
return 2;
}
2_بصورتی شرطی نباید return بشه
int foo()//NRVO fail
{
bool cond;
return cond?1:2;
}
3_سازنده مقداری که بر میگرده نباید side effect داشته باشه
مثلا وقتی که داخل سازنده شی ای که بر میگردونیم از cout استفاده کنیم یا حافظه بگیریم(البته این مورد بستگی به کامپایلر داره خیلی وقت ها بازم حذف میشه )
ضمنا NRVO,RVO قبل از c++11 هم وجود داشتن .
حالا به هر دلیلی اگر NRVO,RVO انجام نشه اگر کلاس move constructor و move assignment operator داشته باشه زمان return شدن از این 2 استفاده میشه(اگر نداشته باشه کپی انجام میشه )
move از کپی کردن به مراتب سریع تر هستش ولی از فرستادن با refrence کمی کند تر (البته بستگی به نحوه پیاده سازی کلاس هم داره )
در مثالی که خودتون زدید اگر با refrence فرستاده بشه زمان صدا زده شدن تابع فقط یک آدرس 4 یا 8 بایتی کپی میشه
void foo(std::vector<int>& a/* inja 1 address copy mishe */)
{
//anjam amaliat rooye a...
}
ولی اگر return بشه و move صدا زده بشه کدی مثل کد زیر اجرا میشه :(کد از هدر vector ویژوال استودیو )
void _Assign_rv(_Myt&& _Right, true_type)
{ // move from _Right, stealing its contents
this->_Swap_all((_Myt&)_Right);//swap all iterators
this->_Myfirst = _Right._Myfirst;
this->_Mylast = _Right._Mylast;
this->_Myend = _Right._Myend;
_Right._Myfirst = pointer();
_Right._Mylast = pointer();
_Right._Myend = pointer();
}
داخل کد بالا خود Swap_all هم چند تا pointer رو کپی میکنه .
یعنی اگر move استفاده بشه زمان return شدن به نسبت زمانی که با آدرس به تابع میفرستیم چند تا پوینتر بیشتر کپی میشه. که عملیات خیلی سریعیه و در صورتی که این تابع چند میلیون بار پشت سر هم اجرا نشه میشه ازش صرف نظر کرد.
پس در مجموع میشه گفت این 2 مورد تفاوتی زیادی با هم ندارن و بعضی وقت ها فرستادن با refrence کمی سریع تره .