Този отговор е за отговор на коментар от @vsoftco
@DarioOO благодаря за връзката. Може ли да напишете кратък отговор? От вашия пример все още не ми е ясно защо std::forward също трябва да бъде дефиниран за rvalues
Накратко:
Тъй като без специализация на rvalue следният код няма да се компилира
#include <utility>
#include <vector>
using namespace std;
class Library
{
vector<int> b;
public:
// hi! only rvalue here :)
Library( vector<int>&& a):b(std::move(a)){
}
};
int main()
{
vector<int> v;
v.push_back(1);
A a( forward<vector<int>>(v));
return 0;
}
но не мога да устоя да пиша повече, така че ето и некратката версия на отговора.
Дълга версия:
Трябва да преместите v
, защото класът Library
няма конструктор, приемащ lvalue към него, а само препратка към rvalue. Без перфектно препращане ще се окажем в нежелано поведение:
функциите за обвиване биха нанесли висока производителност при преминаване на тежки предмети.
със семантиката на преместване гарантираме, че се използва конструктор на преместване, АКО Е ВЪЗМОЖНО. В горния пример, ако премахнем std::forward
, кодът няма да се компилира.
И така, какво всъщност прави forward
? преместване на елемента без нашия консенсус? не!
Той просто създава копие на вектора и го премества. Как можем да сме сигурни в това? Просто се опитайте да получите достъп до елемента.
vector<int> v;
v.push_back(1);
A a( forward<vector<int>>(v)); //what happens here? make a copy and move
std::cout<<v[0]; // OK! std::forward just "adapted" our vector
ако вместо това преместите този елемент
vector<int> v;
v.push_back(1);
A a( std::move(v)); //what happens here? just moved
std::cout<<v[0]; // OUCH! out of bounds exception
Така че това претоварване е необходимо, за да направи възможно имплицитно преобразуване, което все още е безопасно, но не е възможно без претоварването.
Всъщност следният код просто няма да се компилира:
vector<int> v;
v.push_back(1);
A a( v); //try to copy, but not find a lvalue constructor
Реален случай на употреба:
Може да възразите, че аргументите за препращане могат да създадат безполезни копия и по този начин да скрият възможен удар в производителността, да, това всъщност е вярно, но помислете за реални случаи на употреба:
template< typename Impl, typename... SmartPointers>
static std::shared_ptr<void>
instancesFactoryFunction( priv::Context * ctx){
return std::static_pointer_cast<void>( std::make_shared<Impl>(
std::forward< typename SmartPointers::pointerType>(
SmartPointers::resolve(ctx))...
) );
}
Кодът е взет от моята рамка (ред 80): Infectorpp 2
В този случай аргументите се препращат от извикване на функция. Върнатите стойности на SmartPointers::resolve
се преместват правилно, независимо от факта, че конструкторът на Impl
приема rvalue или lvalue (така че няма грешки при компилиране и те така или иначе се преместват).
По принцип можете да използвате std::foward
във всеки случай, в който искате да направите кода по-прост и по-четлив, но трябва да имате предвид 2 точки
- допълнително време за компилиране (не толкова много в действителност)
- може да причини нежелани копия (когато не преместите изрично нещо в нещо, което изисква rvalue)
Ако се използва внимателно, е мощен инструмент.
person
CoffeDeveloper
schedule
02.07.2015
f( prvalue )
, къдетоprvalue
е нещо катоget_value()
, тогава предаватеrvalue
, няма нужда да го препращате. Може би не разбирам какво искаш да кажеш. - person vsoftco   schedule 25.04.2015std::forward
правят по-лесен за изпълнение от други - person wakjah   schedule 25.04.2015rvalue reference
не е rvalue. Не съм сигурен дали това е, което питаш. - person Dean Seo   schedule 02.06.2015std::forward
трябва да бъде дефинирано и за rvalues. - person vsoftco   schedule 02.07.2015