Этот ответ предназначен для ответа на комментарий @vsoftco
@DarioOO спасибо за ссылку. Можешь написать краткий ответ? Из вашего примера мне до сих пор не ясно, почему std::forward также должен быть определен для значений rvalue
Вкратце:
Потому что без специализации 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